Riverpodを使って、リポジトリからデータを取得する場合、StreamかFutureを使うことが多いかと思います。
すると、Providerを使う選択肢としてStreamProvider
かFutureProvider
、AsyncNotifierProvider
を使う選択肢になるかと思います。
AsyncNotifierProvider
とStreamProvider
は更新のタイミングがわかりやすいのですが、
FutureProvider
はどのタイミングで更新されるのか興味があったため調べて整理してみます。
ちなみに、2023/01時点でのプロバイダーの種類は下記です。
- Provider
- StateProvider
- StateNotifierProvider
- FutureProvider
- StreamProvider
- ChangeNotifierProvider(非推奨)
- NotifierProvider
- AsyncNotifierProvider
画面遷移、画面一覧
ボトムナビゲーションバーからホーム画面とデータ一覧画面に遷移できます。
データ一覧画面では、データをリポジトリから取得して表示します。
データの取得は非同期で行うため、取得中はローディングを挟んで一覧が表示されます。
ローディング
データ一覧画面
そして、データ一覧から各データをタップすると、データの詳細画面に遷移できます。
データ詳細画面
// データ一覧を取得するプロバイダー
final dataListProvider =
FutureProvider<List<Data>>((_) => DataRepository.instance.fetchDataList());
// データを取得、更新するリポジトリ
class DataRepository {
static final instance = DataRepository._();
DataRepository._();
final _dataList =
List.generate(10, (index) => Data(id: '$index', name: "Name$index"));
Future<List<Data>> fetchDataList() async {
await Future.delayed(const Duration(seconds: 2));
return _dataList;
}
void changeName(String id, String changedName) {
final index = _dataList.indexWhere((element) => element.id == id);
_dataList[index] = Data(id: id, name: changedName);
}
}
// データクラス
class Data {
String id;
String name;
Data({
required this.id,
required this.name,
});
}
上の状態での動作
まず上のコードでの動作を整理します。
- 初回起動時、データ一覧画面に遷移したときにローディングが走る
- ホーム画面に遷移し、再度データ一覧画面に遷移したときはローディングが走らない
- データ一覧画面からデータ詳細画面に遷移して編集後、データ一覧画面に戻った場合、一覧は更新されない
- 編集後、ホーム画面に一度遷移し再度データ一覧に遷移すると、ローディングは走らないが更新はされている
最後の動作だけよくわかっていないのですが、更新はされているようなので問題ないと思います。
注目すべきは、データ詳細画面で編集後に、データ一覧画面に戻ったときに更新されていないことで、
最終的には、このタイミングで更新されるようにしたいと思います。
autoDisposeを使う
下記のように、プロバイダー部分にautoDispose
を使うとどうなるでしょうか?
// データ一覧を取得するプロバイダー
final dataListProvider =
FutureProvider.autoDispose<List<Data>>((_) => DataRepository.instance.fetchDataList());
この場合、以下の動作になりました。
- 初回起動時、データ一覧画面に遷移したときにローディングが走る
- ホーム画面に遷移し、再度データ一覧画面に遷移したときはローディングが走る
- データ一覧画面からデータ詳細画面に遷移して編集後、データ一覧画面に戻った場合、一覧は更新されない
- 編集後、ホーム画面に一度遷移し再度データ一覧に遷移すると、ローディングが走り、更新はされる
データ詳細画面で編集後に、データ一覧画面に戻ったときに更新されていないこと以外はほぼ期待通りの動作です。
ただ、autoDispose
を使った場合、ホーム画面からデータ一覧画面に遷移するたびにデータの再取得がされるので、この部分は仕様によって変わってくる部分かなと思います。
例えば、このデータの更新がこのユーザー一人だけによって行われる場合はデータ詳細画面からしか編集できないので、データ一覧画面に遷移する度にデータの再取得する必要はありません。
しかし、データ一覧がWeb上で他のユーザーにもリアルタイムで更新される場合はデータ一覧画面に遷移するタイミングで更新できた方が良いのかなと思います。
invalidateを使ってみる
下記は、データの名前を変更するWidgetで、onPressed
でリポジトリのデータを更新したあと、
ref.invalidate(dataListProvider);
でプロバイダをリフレッシュしています。
ElevatedButton(
onPressed: () {
DataRepository.instance.changeName(widget.data.id, textController!.text);
ref.invalidate(dataListProvider);
},
child: const Text("Change"),
),
そして、プロパイダー部分はautoDispose
を外しています。
// データ一覧を取得するプロバイダー
final dataListProvider =
FutureProvider<List<Data>>((_) => DataRepository.instance.fetchDataList());
この場合、以下の動作になりました。
- 初回起動時、データ一覧画面に遷移したときにローディングが走る
- ホーム画面に遷移し、再度データ一覧画面に遷移したときはローディングが走らない
- データ一覧画面からデータ詳細画面に遷移して編集後、データ一覧画面に戻った場合、**一覧は更新される
(上で更新されているため、以下の動作は意味がないため、割愛します。
(編集後、ホーム画面に一度遷移し再度データ一覧に遷移すると、ローディングが走らない、更新は上でされている)
まとめ
どこでみたのか忘れたのですが、FutureProvider
の使い所として、非同期で一回だけ取得するところで使うという記事を見ました。
上記の使い方でも問題ないですが、明示的に更新したい場合はinvalidate
を使っていくと良さそうです。