お久しぶりです。今回は前回に引き続きriverpodを読んでいたので、メモ代わりに意訳をまとめました。
今回まとめたのは、以下の二つです。
Providerをreadする
このガイドでは、どうやってProviderを使用するのかを知ることができる
providerをreadする方法はいくつかあって、基準・規格・仕様によって少し違う 手短に知るには以下の図にそってどのproviderをreadするかみてね
どこでProviderをreadするか? - テストやDart-Only-PackageでreadするならProviderContainer.read
Widgetの中
- Dialog表示のためにlisteningするならProviderListener
onPressed
を覗くbuild
メソッドの中でreadしなければContext.read
- flutter-hooksを使うなら
useProvider
- flutter-hooksを使わないなら
Consumer
他のProviderの中
- providerの更新に伴ってproviderによって公開された値が、再生成されるべきか
- べきなら
ProviderReference.watch
- ちがうなら
providerReference.read
- べきなら
- providerの更新に伴ってproviderによって公開された値が、再生成されるべきか
つぎにそれぞれ個別の場合をみながらどうやって動作するのか紹介します
このガイドでは以下のproviderで考えます。
final counterProvider = StateProvider((ref) => 0);
readの方法を決定する
listenしたいProviderによって、値をlistenする方法がいくつかあります
たとえば、以下のStreamProviderについてだと、
final userProvider = Streamprovider<User>(...);
userProviderをreadするとき、以下のような方法が可能である。
- userProvider自身で同期的に現在の状態をreadする
Widget build(BuildContext context, ScopeReader watch) { AsyncValue<User> user = watch(userProvider); return user.when( loading: () => const CircularProgressIndicator(), error: (error, stack) => const Text('Oops'), data: (user) => Text(user.name), ); }
- Streamの連携に伴って変更される値をreadするなら
userProvider.stream
Widget build(BuildContext context, ScopeReader watch) { Stream<User> user = watch(userProvider.stream); }
- 最新の値で発火されるFutureで解決される値を取得するには、
userProvider.last
を使用する
Widget build(BuildContext context, Scopedreader watch) { Future<User> user = watch(userProvider.last); }
さらなる情報はAPIリファレンスみてね
ProviderをWidgetの中で使う
この章では、ProviderをWidgetの中で使う方法について知ることができる
ConsumerWidget
ConsumerWidget
はStatelessWidget
のような基底クラスだ。そして、providerをlistenすることができる
class Home extends ConsumerWidget { @override Widget build(BuildContext context, Scopedreader watch) { int count = watch(counterProvider).state; return Scaffold( appBar: AppBar(title: const Text('Counter example')); body: Center( child: Text('$count'), ), ); } }
この例では、counterProvider
とcounterが変更される旅にText
がリビルドされるだろう。
counterProviderの値によって気づくことは、関数がwatchを呼ぶことだ。ConsumerWidget
で作られた関数は私たちのproviderをlistenし、値の変更が公開されるとリビルドする。
Caution
ConsumerWidgetの引数を渡すwatch()
は```onPressedのような関数では非同期で呼ばないべきだ。
もしユーザのイベントのレスポンスをproviderをreadする必要があるなr、myProvider.read(BuildContext)
を使用してください
Consumer
Consumer
はアプリケーション内でデータを扱う特定のウィジェットの再描画のパフォーマンスを最適化することができるConsumerWidget
だ
たとえば、ConsumerWidgetのスニペットを更新以下のコードでは、まえもってText
のみが再描画されることを認識できる。
class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Counter example')), body: Center( child: Consumer( builder: (context, watch, child) { int count = watch(counterProvider).state; return Text('$count'); }, ), ), ); } }
context.read(myProvider)
ときには、listenする値が存在しないこともある。例えば、オブジェクトはonPressed
でのみ必要な場合。
私たちはConsumer
を使うことができる
Consumer(builder: (context, watch, _) { StateController<int> counter = watch(counterProvider); return RaisedButton( onPressed: () => counter.state++, child: Text('increment'); ) });
しかし、これは効果的ではない。providerのlistenによって、counterが変更される旅にRaisedButtonが再描画される。counterが実際にbuildに使われない場合でさえ。
解決方法としてcontext.read(myProvider)
が存在する。
これを使うことで前述のコードを以下のようにリファクターできる。
@override Widget build(BuildContext context) { retur RaisedButton( onPressed: () => context.read(counterProvider).state++, child: Text('increment'), ); }
こうすることで、依然として、クリックすることで、ボタンはcounterをインクリメントする。しかし、もはやproviderをlistenしておらず、不必要な再描画を避けている。
context.read
がみつからない。どうしたらいい?
もしcontext.read
が見つからないなら、それはおそらく正しいパッケージをインポートしていないからだ。このメソッドを使うにはpackage:flutter_riverpod
か、package:hooks_riverpod
をインポートしなければならない。
Info
listenしているProviderによっては、これは必要ないかもしれない。
たとえば、StateNotifierProvider
はそれ自身の状態のlistenを除く、StateNotifierを取得する方法が用意されている。
class Counter extends StateNotifier<int> { Counter(): super(0); void increment() => state++; } final counterProvider = StateNotifierProvider((ref) => Counter()); // ... @override Widget build(BuildContext context, ScopedReader watch) { final Counter counter = watch(counterProvider); return RaisedButton( onPressed: counter.increment, child: Text('increment'), ); }
Caution
context.read
をbuild()
の中で呼ぶことを回避してください。もし、再描画を最適化したいなら、値の変更のlistenをProviderの中に変更してください。
ProviderListener
ときには、Providerの変更のあとでダイアログの表示やウィジェットツリーへのルートの挿入を行いたい場合もある。
ProviderListener
ウィジェットを利用して以下のように実装してください
Widget build(BuildContext context) { return ProviderListener<StateController<int>>( provider: counterProvider, onChange: (counter) { if(counter.state == 5) { show Dialog(...); } }, child: Whatever(), ) }
これできっとカウントが5になるとダイアログが表示されます。
Providerの中で他のProviderをreadする
一般的なProviderの作成方法として他のオブジェクトからオブジェクトを生成することがある。
例えば、UserRepository
からUserController
を生成したい時、それらは違ったProviderによって公開されているとする。
このシナリオはproviderがパラメータを受け取るためのref
を使うことによって可能になる
final userRepositoryProvider = Provider((ref) => UserRepository()); final userControllerProvider = StateNotifierProvider((ref) { return UserController( repository: ref.watch(userRepositoryProvider), ); });
CombineProviders
ガイドでより多くの情報を得られる。例えば以下のように
- 時間と共に変化する値からオブジェクトを作る時、どんなことがおこるか
- 良い方法について