😇

ref.listenの使い道

2024/07/11に公開

対象者

  • riverpodを使ってる人
  • ref.watch, ref.readしか使ってない人

ref.listen使ってる人ってどれくらいいますかね???
公式見てるけど、使い方がよくわかりません???

https://riverpod.dev/zh-hans/docs/concepts/reading

やることは、Widgetのコードを書く。スナックバー、ダイアログ、画面遷移かな。

プロジェクトの説明

ソケット通信のようなアプリケーションを使って使う例をご紹介します。while文で、数字が増えていき5になったら、スナックバーを表示します。

俺ちゃんは以前、Notifierでないと、ref.listen使えないと思っていたのですが、どうやらStreamProviderでも使えるらしく最近色々試しております。

listenとは、英語で聞くという 意味だそうです。アプリがビルドされると、カウンターが増えて、数字が5になると、ref.listenの中に、書いてある分岐処理で、条件が一致すると、スナックバーを表示する仕組みになってます。

動画では、ソケット通信。仮だろうけど、カウンターが増えていくロジックが実行されます。このカウンターが増えている状態を「聞いて」 値 == 5になったら、スナックバーを表示します。

riverpodだと、ロジックだけのクラスやプロバイダーの中に、スナックバーなどの、UIのコードを書くことはありません。わけないとカッコ悪いし、使う意味がない。分離させたいから使う。そのために、ref.listenを使います。これを使えば、ConsumerWidgetの中に書くので、UIのコードに書くから、違和感もないし、コードがあるのは、UI側なので、画面に関係したロジックだけあると、考えれば分離はできていそう。

私の場合は、エラー処理をref.listenで書いたことありますが、最近は、mixinで作ったクラスを多重継承させて、スナックバーのエラーを出すロジックを使ってますね。「この辺は好みかもしれないです」

[なぜかソケットと呼ばれてコード]

abstract class WebsocketClient {
  Stream<int> getCounterStream();
}

class WebsocketImpl implements WebsocketClient {
  
  Stream<int> getCounterStream() async* {
    int i = 0;
    while (true) {
      await Future.delayed(const Duration(microseconds: 500));
      yield i++;
    }
  }
}


WebsocketClient websocketClient(WebsocketClientRef ref) {
  return WebsocketImpl();
}


Stream<int> counter(CounterRef ref) {
  final websocketClient = ref.watch(websocketClientProvider);
  return websocketClient.getCounterStream();
}

riverpod v2で、ref.watchした変数の値を表示する場合は、switchを使います。riverpod v3からは、これで書くのが推奨されるとか???
なので、私は最近は、.whenを使っておりません。この間仕事で、全部、when -> switch へ移行しました。なぜか、Supabasaeのデータが表示されないパーツがあった😇

ref.invalidateを実行すると、カウンターの値がリセットされます。これはプロバイダーを強制的に更新する機能みたいですね。ボタンを押すと画面が更新されて、値もリセットされます。

[全体のコード]

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'websocket_view.g.dart';

// flutter pub run build_runner watch --delete-conflicting-outputs
abstract class WebsocketClient {
  Stream<int> getCounterStream();
}

class WebsocketImpl implements WebsocketClient {
  
  Stream<int> getCounterStream() async* {
    int i = 0;
    while (true) {
      await Future.delayed(const Duration(microseconds: 500));
      yield i++;
    }
  }
}


WebsocketClient websocketClient(WebsocketClientRef ref) {
  return WebsocketImpl();
}


Stream<int> counter(CounterRef ref) {
  final websocketClient = ref.watch(websocketClientProvider);
  return websocketClient.getCounterStream();
}

class WebsocketView extends ConsumerWidget {
  const WebsocketView({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final AsyncValue<int> counter = ref.watch(counterProvider);
    ref.listen<AsyncValue<int>>(counterProvider, (prev, next) {
      // 値が5になったら🎉を表示する
      if (next.asData?.value == 5) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('値が5になりました🎉')),
        );
      }
    });

    return Scaffold(
      appBar: AppBar(
        title: const Text('Websocket Example'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.invalidate(counterProvider);
        },
        child: const Icon(Icons.refresh),
      ),
      body: switch (counter) {
        AsyncData(:final value) => value == 1
            ? const Center(child: Text('値が1になりました🎉'))
            : Center(child: Text('Value: $value')),
        AsyncError(:final value) => Center(child: Text('Error: $value')),
        _ => const Center(child: CircularProgressIndicator()),
      },
    );
  }
}

感想

いかがでしたでしょうか。あまり情報ないので、いつも海外の動画のチュートリアルを参考にしつつ、今だとriverpod generaltorで書くのが一般的になってきたので、こちらでロジックを作ってみました。他にも色々試してみたいロジックはあるので、何か作ってみたらまた記事を書いてみようと思います。

参考になった海外サイト

Discussion