Riverpodのドキュメントを意訳してみた2
お久しぶりです。今回は前回に引き続きriverpodを読んでいたので、メモ代わりに意訳をまとめました。今回まとめたのは、以下の二つです。
お久しぶりです。今回は前回に引き続き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ガイドでより多くの情報を得られる。例えば以下のように - 時間と共に変化する値からオブジェクトを作る時、どんなことがおこるか - 良い方法について
-––