Providerとは
Riverpodにおいて、変更を通知することができる状態管理のためのクラス。"ref"というオブジェクトを介して、状態をProviderの内外で参照することができる。
refのwatch, listen, readの違い
サンプルコード
class Person {
final String name;
final int age;
Person({required this.name, required this.age});
}
final personProvider = StateProvider((ref) => Person(name: "nemo", age: 15));
class TopPage extends ConsumerStatefulWidget {
const TopPage({Key? key}) : super(key: key);
@override
ConsumerState<TopPage> createState() => _TopPageState();
}
class _TopPageState extends ConsumerState<TopPage> {
@override
Widget build(BuildContext context) {
final Person person = ref.watch(personProvider);
ref.listen(personProvider, (previous, next) {
print('previous: $previous, next: $next');
});
return Column(
children: [
Text(person.name),
ElevatedButton(
onPressed: () {
ref.read(personProvider.notifier).state = Person(name: "nemo", age: 16);
},
child: const Text('Increment'),
),
],
);
}
}
以上が、ref.watch, listen, readの全てを盛り込んだコード。
watch
ref.watchはProviderの状態を監視し、変更が起こった際にWidgetの再レンダリングをする。
サンプルコードであれば、personProviderが持っているPersonクラスの値が更新されたタイミングでTopPageの再レンダリングを行う。
Providerから生えているselect関数を使うと、この再レンダリングをより効果的なタイミングで行うことができるようになる。サンプルコードを考えると、Textとして表示しているのは、Personのnameプロパティの値だけであり、personProviderの状態のageプロパティだけが変更された場合は再レンダリングを行うのは不必要である。
そのような場合は、サンプルコードのref.watchを行っている行のコードを
final Person person = ref.watch(personProvider.select((person) => person.name));
のようにしてあげることで、nameプロパティの値が変更された時のみ再レンダリングを走らせるようにすることができる。
listen
ref.listenはref.watchと同様に、Providerの状態を監視する。しかしref.watchと違い、状態の更新を検知したときに再レンダリングではなく、引数で渡したCallbackの処理を行う。
第一引数と第二引数で、それぞれ一個前の状態と最新の状態の両方を取得することができる。
ref.listenは主に、Snackbarの表示やエラーダイアログの表示などに使用される。
read
ref.readは1番単純に、Provider状態を取得する。watchやlistenのように監視することはなく、readを行った時点での状態をそのまま取得する。
注意点
基本的にはref.readは使わず、ref.watch, listenを使用するのが好ましい。ref.watch, listenでは実現不可能なケースにのみref.readを使うべきである。
参考文献