Open5
Riverpodで複数のProviderを監視して扱う方法
Riverpodで複数のProviderを扱うときに書き方に迷ったのでまとめる
例えば次のようなProviderがある
Future<String> getStringFromAPI(GetStringFromAPIRef ref) async {
// 3秒待つ
await Future.delayed(const Duration(seconds: 3));
return Future.value('Hello from the API');
}
1つのProviderだけなら、whenやswitchで監視できる
final asyncValue = ref.watch(getStringFromAPIProvider);
// whenを使う場合
asyncValue.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stackTrace) => Center(child: Text('Error $error')),
data: (text) => Text(text)
);
// switchを使う場合
switch(asyncValue) {
AsyncData(:final value) => Text(value),
AsyncError(:final error) => Center(child: Text('Error $error')),
_ => const Center(child: CircularProgressIndicator())
}
switch
の方はDartのswitch式を利用している。
取りうる値を網羅する必要があるので、AsyncLoadingではなく_
と書いて上記以外の場合はローディングを表示させている
AsyncDataのvalueなどは、他の変数名は使えないので注意
複数のProviderを使う場合、素直に書くなら次のようになる
final asyncValue = ref.watch(getStringFromAPIProvider);
final asyncValue2 = ref.watch(getStringFromAPIProvider2);
asyncValue.when(
data: (text) => asyncValue2.when(
data: (text2) => ...
)
is
を使うともう少しわかりやすく書ける
final asyncValue = ref.watch(getStringFromAPIProvider);
final asyncValue2 = ref.watch(getStringFromAPIProvider2);
// AsyncLoadingより先に書かないと、一方が読込中の場合にエラーが発生してもエラーが表示されない
if (asyncValue is AsyncError || asyncValue2 is AsyncError) {
return Center(child: Text('Error'));
}
// いずれかが読込中の場合
if (asyncValue is AsyncLoading || asyncValue2 is AsyncLoading) {
return const Center(child: CircularProgressIndicator());
}
// 読み込み完了後
return Text(asyncValue is AsyncData ? asyncValue.data : ''); // このあたりは書き方が正しいか微妙
読み込み完了後の部分では、AsyncError
やAsyncLoading
ではないことが確定しているはず。なので、when
やswitch
は長くなるし使いたくない。