Riverpod 3 入門 ~AsyncNotifier編~
AsyncNotifierProviderの簡単な説明です。今回もgenerator未使用です。
AsyncNotifierProviderとは?
AsyncNotifierProviderは、非同期処理が可能で、stateの変更が可能なproviderです。
WebAPI経由のCRUD操作やsqflite操作のサンプルでよく利用されています。
| Synchronous | Future | Stream | |
| Unmodifiable | Provider | FutureProvider | StreamProvider |
| Modifiable | NotifierProvider | AsyncNotifierProvider | StreamNotifierProvider |
環境
- Flutter 3.38.6
- flutter_riverpod 3.2.0
Install
flutter_riverpod パッケージのインストール
Bash
flutter pub add flutter_riverpodlint設定
YAML
plugins:
riverpod_lint: ^3.1.2AsyncNotifierProvider
巷では、Riverpod3以降のサンプルも少なく、ChatGPTもClaudeもGitHub Copilotもデータが少ないのか適当言ってきますが頑張りましょう。
とっても簡単なproviderを作ります。
カウンター機能を持ったproviderです。
ファイル名:counter_provider.dart
counter_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
class Counter extends AsyncNotifier<int> {
@override
Future<int> build() async {
return 0;
}
Future<void> increment() async {
state = const AsyncValue.loading(); // お好みで
await Future.delayed(const Duration(milliseconds: 800));
state = AsyncValue.data((state.value ?? 0) + 1);
}
}
// AsyncNotifierProviderの定義
final counterProvider = AsyncNotifierProvider<Counter, int>(() {
return Counter();
});
ちょっとわざとらしくwaitがありますね。
さて、これでもいいのですが、本当はここにAPI取得とかDB操作とか記述されます。例外対応もしておきたいで、try/catch構文を追加してみましょう。
increment()
state = const AsyncValue.loading();
try {
await Future.delayed(const Duration(milliseconds: 800));
state = AsyncValue.data((state.value ?? 0) + 1);
} catch (e, st) {
state = AsyncValue.error(e, st);
}AsyncValueにはguardが使えますね。
次のように変更してみましょう。
increment()
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
await Future.delayed(const Duration(milliseconds: 800));
return (state.value ?? 0) + 1;
});「state = const AsyncValue.loading();」は外してもいいです。長い処理のときはあったほうが親切かもね。
main側
ConsumerWidgetで受信します。
loading中はボタンを押せないようにしています。
main code
Dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'counter_provider.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AsyncNotifierProvider',
home: const MyHomePage(),
);
}
}
class MyHomePage extends ConsumerWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// watch!!
final counterAsync = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(title: const Text('非同期カウンター')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('ボタンを押すと数秒後にカウントアップします'),
const SizedBox(height: 20),
counterAsync.when(
data: (count) => Text(
'$count',
style: Theme.of(context).textTheme.headlineLarge,
),
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text('エラー: $error'),
),
const SizedBox(height: 20),
ElevatedButton.icon(
onPressed: counterAsync.isLoading
? null
: () => ref.invalidate(counterProvider),
icon: const Icon(Icons.refresh),
label: const Text('リフレッシュ'),
),
],
),
),
floatingActionButton: FloatingActionButton(
// これは、多重に押せてしまう。
// onPressed: () => ref.read(counterProvider.notifier).increment(),
//
onPressed: counterAsync.isLoading
? null
: () => ref.read(counterProvider.notifier).increment(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
参考サイト
- flutter_riverpod pub.dev
- Riverpod.dev 公式サイト
- riverpod_lint