🐟

【Riverpod学習】Providerの種類〜StreamProviderについて〜

2024/10/13に公開

以下の公式を読みながらStreamProviderについて勉強したのでその備忘録です

https://riverpod.dev/docs/providers/stream_provider

StreamProviderは、非同期で継続的にデータを受け取るストリーム(Stream)を扱うためのRiverpodプロバイダです。これは、FutureProviderと似ていますが、Streamの更新が繰り返し行われる点で異なります。データが定期的に更新されたり、リアルタイムで変化する場合に利用されます。

1. StreamProviderの概要

StreamProviderは、リアルタイムデータや継続的なデータ更新を扱う際に役立ちます。例えば、FirebaseのリアルタイムデータやWebSocketを使ったライブチャット、定期的な更新を行うAPIのデータなど、繰り返しデータを受け取るシチュエーションで使われます。
例えばFirebaseのリアルタイムデータや定期的にデータを更新するAPIなどです

2. StreamProviderの利点

  • ref.watchで他のプロバイダがストリームを監視できる: StreamProviderは他のプロバイダと組み合わせて使うことができ、データの変化を自動的に監視します。
  • AsyncValueを使用して、エラーやローディング状態の処理が簡単: StreamProviderはAsyncValueを返すため、loadingやerrorの状態を簡単に処理できます。
  • 最新のデータをキャッシュ: ストリームが新しい値を発行しても、途中から監視を始めたリスナーにも最新のデータがすぐに提供されます。
  • Streamのモックが簡単: テストの際に、ストリームを簡単にモック(仮想データで置き換え)することができます。

3. 例: ライブチャットの実装

以下の例では、StreamProviderを使ってWebSocketを介したライブチャットの実装を行います。サーバーと接続し、新しいメッセージが届くたびにリストを更新します。

1. 非同期ストリーム関数の定義

まず、サーバーとの接続をストリームで扱う非同期関数を定義します。


Stream<List<String>> chat(ChatRef ref) async* {
  // WebSocketでAPIに接続
  final socket = await Socket.connect('my-api', 4242);
  
  // プロバイダが破棄されるときにソケットを閉じる
  ref.onDispose(socket.close);

  // メッセージのリストを保持する
  var allMessages = const <String>[];
  
  // ソケットからの新しいメッセージを監視し、受信したメッセージをリストに追加
  await for (final message in socket.map(utf8.decode)) {
    allMessages = [...allMessages, message];
    yield allMessages;
  }
}

このコードでは、Socket.connectでサーバーに接続し、新しいメッセージが届くたびにallMessagesリストに追加します。新しいメッセージが届くたびにyieldでリストを更新し、StreamProviderを介してUIに通知されます。

2. UIでの使用

次に、このストリームをUIで監視し、メッセージをリアルタイムで表示します。

Widget build(BuildContext context, WidgetRef ref) {
  // chatProviderのストリームを監視
  final liveChats = ref.watch(chatProvider);

  // 状態に応じて異なるUIを表示
  return switch (liveChats) {
    // メッセージが取得できた場合
    AsyncData(:final value) => ListView.builder(
        reverse: true,  // 新しいメッセージを下に表示
        itemCount: value.length,
        itemBuilder: (context, index) {
          final message = value[index];
          return Text(message);
        },
      ),
    // エラーが発生した場合
    AsyncError(:final error) => Text('Error: $error'),
    // データがまだ読み込まれていない場合
    _ => const CircularProgressIndicator(),  // ローディングインジケーターを表示
  };
}
  • AsyncData: ストリームからメッセージが正常に受信できた場合、メッセージをリストに表示します。
  • AsyncError: ストリームが何らかの理由で失敗した場合、エラーメッセージを表示します。
  • loading(_): データがまだ読み込まれていないときに、ローディングインジケーターを表示します。

4. 使い分けと注意点

StreamProviderを使うべきケース:

  • リアルタイムデータの監視: ライブチャットや、センサーのデータ、天気予報の定期的な更新など、リアルタイムでデータを受信する必要がある場合。
  • ストリームのキャッシュと再利用: 同じデータを複数のウィジェットで再利用する場合にも便利です。StreamProviderは、過去に受信したデータをキャッシュし、途中から接続したウィジェットにも最新のデータを提供します。

FutureProviderとの違い:

FutureProviderは、一度非同期処理を実行し、その結果を取得するのに使いますが、StreamProviderは継続的にデータを受信し続ける場合に適しています。
例えば、APIから一度だけデータを取得して終わりにするならFutureProviderを使いますが、データがリアルタイムで更新される場合や、定期的に変わるデータを扱う場合はStreamProviderを使います。

5. まとめ

StreamProviderは、リアルタイムデータや継続的に変化するデータを扱うためのプロバイダです。
エラーハンドリングやローディング状態の管理が簡単で、UIを自動的に更新できます。
FirebaseやWebSocket、天気情報APIなど、定期的に更新されるデータの監視に適しています。
StreamProviderを使うことで、アプリが継続的にデータを取得する必要がある場面(チャットアプリ、センサーのデータなど)で効率的に状態を管理でき、複雑なデータ更新も簡単に処理できます。

Discussion