2024/10/13 追記
Riverpod 2.0のAsyncNotifierProviderを使用するのがベストプラクティスっぽいです。
dependencies:
flutter_riverpod: ^2.5.1
riverpod_annotation: ^2.3.5
riverpod_generator: ^2.4.0
ますは必要なライブラリをインポートします。
part 'item_list_provider.g.dart';
@riverpod
class ItemList extends _$ItemList {
@override
Future<List<Item>> build() async {
//初期化メソッドを追加
final List<Item> initialItem = await ref.watch(hogeProvider).fetch();
return initialItem;
}
};
Future<void> addItem({required Item newItem}) async {
final previousList = await future;
state = AsyncData([...previousList, newItem]);
}
}
riverpod_generatorでproviderを生成するためのファイルを作成します。
・part 〜 の部分はファイル名.g.dartとします。
・riverpod_annotation をimportしたことで@riverpod
アノテーションが利用できます。
・freezedと同じような構文で _$クラス名 をextendsしてあげます
・buildメソッドをoverrideします。buildはproviderが初めて呼び出された時に実行される初期化メソッドです。(async/awaitが利用できる!)
・addItemのようにメソッドを追加できます。futureは予約変数になっており、現在のstateのFuture<List<Item>>
が取得できます。awaitでList<Item>
にしてあげています。
・stateはAsyncValue<List<Item>>
型なのでAsyncValue()でラップしてあげています。
flutter pub run build_runner build --delete-conflicting-outputs
ターミナルからbuild_runnerで上記コマンドを実行してあげると、dart item_list_provider.dartと同じ階層にitem_list_provider.g.dartが作成されます。
ref.watch(itemListProvider);
作成されたproviderはいつも通り上記コードで呼び出すことができます。
--- 追記ここまで ---
StateNotifierProviderはasyncを使えない…
StateNotifierの初期値にDBや内部ストレージから非同期で取得した値をセットしたい!
というケースは多々あると思います。
class FormItemList extends StateNotifier<List<Item>> {
FormItemList(List<Item> initialState) : super(initialState);
void set({required List<Item> itemList}) {
state = itemList;
}
Future<List<Item>> addNewItem({
required List<Item> itemList,
}) async {
state = [...state, ...itemList];
return state;
}
}
このようなStateNoriferをproviderで提供するとします。
非同期関数で取得した値を初期値として渡してあげる場合、以下のように書きたくなりますが、StateNotifierProviderの初期化メソッド内ではasync/awaitを使用できません。
final formItemProvider = StateNotifierProvider<FormItemList, List<Item>>((ref) async{
// NG
await initialItem = ref.watch(hogeProvider).fetch();
return FormItemList(initialItem);
});
これと同様のことを実現するには、まずStateNotifierに初期化用のメソッドを用意してあげます。
class FormItemList extends StateNotifier<List<Item>> {
FormItemList(List<Item> initialState) : super(initialState);
//初期化メソッドを追加
Future<void> initialize({required Ref ref}) async {
// ここで非同期処理を行う
final List<Item> initialItem = await ref.watch(hogeProvider).fetch();
state = initialItem
}
// 以下略
}
StateNotifierProviderの初期化時にこのメソッドを呼んであげればOKです
final formItemProvider = StateNotifierProvider<FormItemList, List<Item>>((ref) {
//空のリストでインスタンスを作成
final notifier = FormItemList([]);
//初期化メソッドを呼び出す
notifier.initialize(ref: ref);
return notifier;
});
このように記述することでformItemProviderの初回呼び出し時に、非同期で取得した初期値をセットしたStateNotifierを提供することができます。
当たり前といえば当たり前な方法ですね!