強制的にProviderを更新させる context.refresh
FutureProviderでは、非同期処理で値を取得できます。
例えば、APIからのJSON取得です。
しかし、Streamと違ってFutureの場合は1度取得が終わったら、通常更新はされません。
手動でもう一度非同期処理を実行させて、最新の値を取得したい場合はどうしたら良いのでしょうか?
context.refresh
を使うことで実現できます。
refreshメソッド
RefreshIndicatorを使ってAPIをリロードする例
FutureProviderを使ってGitHubのAPIを実行して結果をMap型で返すProviderを作ります。
import 'dart:convert';
import 'package:http/http.dart' as http;
final githubUserProvider = FutureProvider<Map<String, Object?>>((ref) async {
final response = await http.get(Uri.https(
'api.github.com',
'users/$username',
));
return json.decode(response.body);
});
Widget側でFutureProviderを watch
します。
FutureProviderは .future
を付けることで Future型として取得することもできますが、
Riverpodを使用しているなら AsyncValue<T>
型[1]として使った方が素直です。
.when
を使うことで loading: 読み込み中
, error: エラー発生
の状態を忘れずに処理することができます。
非同期処理が完了して取得したデータは data
で受け取りハンドリングします。
Widget build(BuildContext context, ScopedReader watch) {
return watch(githubUserProvider).when(
// 非同期処理実行中はインジケーターを表示しておく
loading: () => const CircularProgressIndicator(),
// エラーが発生した場合はエラー情報を表示します。
// ここでもボタンを表示して `context.refresh` できるようにすると親切ですね。
error: (error, stack) => Text('Error: $error, $stack'),
// 非同期処理が完了したら `data` プロパティの引数として受け取れる。
data: (user) {
return RefreshIndicator(
// RefreshIndicatorでListView等を囲み、 `onRefresh` で動作を指定すれば引っ張って更新できるようになる
// `context.refresh()` でProviderを更新します。
onRefresh: () => context.refresh(_githubUserProvider),
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
children: [
ListTile(
title: const Text('login'),
subtitle: Text('${user['login'] ?? 'none'}'),
),
ListTile(
title: const Text('id'),
subtitle: Text('${user['id'] ?? 'none'}'),
),
ListTile(
title: const Text('html_url'),
subtitle: Text('${user['html_url'] ?? 'none'}'),
),
],
),
);
},
);
}
AsyncValue<T>
使用時に、 loading:
や error:
のハンドリングが不要な時は、
.data?.value
と書くことで、 loading or error
状態の時は null
が、非同期処理が完了すれば値が取得できるのでそちらを使いましょう。
// Map<String, Object?>? 型となるので、 利用時にnullチェックが必要です
final user = watch(githubUserProvider).data?.value;