Open5

Riverpodで複数のProviderを監視して扱う方法

ak2ieak2ie

Riverpodで複数のProviderを扱うときに書き方に迷ったのでまとめる

ak2ieak2ie

例えば次のようなProviderがある


Future<String> getStringFromAPI(GetStringFromAPIRef ref) async {
  // 3秒待つ
  await Future.delayed(const Duration(seconds: 3));

  return Future.value('Hello from the API');
}
ak2ieak2ie

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などは、他の変数名は使えないので注意

ak2ieak2ie

複数のProviderを使う場合、素直に書くなら次のようになる

final asyncValue = ref.watch(getStringFromAPIProvider);
final asyncValue2 = ref.watch(getStringFromAPIProvider2);

asyncValue.when(
    data: (text) => asyncValue2.when(
        data: (text2) => ...
)
ak2ieak2ie

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 : ''); // このあたりは書き方が正しいか微妙

読み込み完了後の部分では、AsyncErrorAsyncLoadingではないことが確定しているはず。なので、whenswitchは長くなるし使いたくない。