ref.listenの使い道
対象者
- riverpodを使ってる人
- ref.watch, ref.readしか使ってない人
ref.listen使ってる人ってどれくらいいますかね???
公式見てるけど、使い方がよくわかりません???
やることは、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