Riverpod3入門 Generatorコード生成編
Generatorを使用してriverpodのコードを生成してみます。
目次
Riverpod_generatorとは?
Generatorを使用することにより、Riverpodでよくみるボイラーテンプレート少なくします。
build_runnerでコードを生成します。
final fetchUserProvider = FutureProvider.autoDispose.family<User, int>((
ref,
userId,
) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
});これがこんな感じで書けます。
@riverpod
Future<User> fetchUser(Ref ref, {required int userId}) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
}About code generation によると、コード生成をお勧めしている様ですね。
At the same time, Riverpod embraces code generation and recommends using it.
同時に、Riverpodはコード生成を採用し、それを使用することを推奨しています。
@riverpodアノテーションを使用してコードが自動生成されるとき、使うべきProviderはシグネチャから自動判別されます。
環境
- Flutter 3.41.2
- flutter_riverpod 3.2.1
- Apple M4 / Tahoe 26.3
Riverpod Genenator
Install Package
パッケージをインストールします。
flutter pub add flutter_riverpod
flutter pub add riverpod_annotation
flutter pub add dev:riverpod_generator
flutter pub add dev:build_runnerこっちのほうが早いですね。
flutter pub add flutter_riverpod riverpod_annotation dev:riverpod_generator dev:build_runnerpubspec.yamlが更新されます。
補足)Riverpod公式の例ではbuild_runnerにはVersionを指定せずに使用していました。
lintを設定します。
- analysis_options.yaml で設定する
- riverpod_lint の最新Versionを指定する
plugins:
riverpod_lint: 3.1.3lint設定後に解析が始まります。少し経つと、main.dartにprovider scope が見つからないと警告が発生します。後で修正しましょう。
Build runner の起動
ファイルの変更を監視してコードを生成してくれます。
build_runnerを起動しておきましょう。
dart run build_runner watch -dmain.dartの編集
さて、lintがうまく設定されていれば、main.dartに警告が発生していると思います。
main.dartを修正しましょう。
importとProviderScopeを追加します。
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(ProviderScope(child: const MyApp()));
}Providerの作成
それでは簡単なproviderを定義してみましょう。
hello_world.dartを新規作成します。
コードを記述していくと、part 'hello_world.g.dart'; の部分はエラーになりますが、ファイルが自動生成されればエラーは消えます。
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'hello_world.g.dart';
@riverpod
String greeting(Ref ref) {
return 'Hello World';
}保存すると、「hello_world.g.dart」ファイルが生成されたはずです。
そして、エラーが消えます。
Providerのwatch
Consumer でwatchしてみましょう。
import 'hello_world.dart';
// ~~~
Consumer(
builder: (context, ref, child) {
final greeting = ref.watch(greetingProvider);
return Text(
greeting,
style: Theme.of(context).textTheme.headlineMedium,
);
},
), // Consumerもちろん、ConsumerWidget を継承してもいいでしょう。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'hello_world.dart';
class HelloPage extends ConsumerWidget {
const HelloPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final greeting = ref.watch(greetingProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Hello Page'),
),
body: Center(
child: Text(
greeting,
style: Theme.of(context).textTheme.headlineMedium,
),
),
);
}
}Providerで提供しようとした文字列が表示できれば成功です。
いろいろなProvider
いろいろなProviderを作成してみましょう。
FutureProvider
お時間があれば、await でも入れてみましょう。
@riverpod
Future<String> fetchUserName(Ref ref) async {
// await Future.delayed(Duration(seconds: 1));
return 'John Doe';
}NotifierProvider
こちらはカウンターのデモです。
@riverpod
class Counter extends _$Counter {
@override
int build() {
return 0; // 初期値
}
void increment() {
state++;
}
void decrement() {
state--;
}
void reset() {
state = 0;
}
}状態を保持する場合はkeepAlive: true の指定が必要になります。
@Riverpod(keepAlive: true)
class Counter extends _$Counter {
// ~
}AsyncNotifierProvider
@riverpod
class UserProfile extends _$UserProfile {
@override
Future<String> build() async {
// 非同期で初期データを取得
// await Future.delayed(Duration(seconds: 1));
return 'Initial User';
}
Future<void> updateName(String newName) async {
state = AsyncValue.loading();
state = await AsyncValue.guard(() async {
// await Future.delayed(Duration(milliseconds: 500));
return newName;
});
}
}KeepAliveについて
コード生成を使う時、provider はデフォルトで自動破棄されます。
自動破棄を無効にしたい場合は、keepAlive: trueアノテーションが必要です。
コード生成しないときは必要ありませんでしたが、コード生成の場合はautoDisposeが標準なのです。
状態を維持する場合は付けましょう。
@Riverpod(keepAlive: true)
“R”iverpodと頭が大文字で間違いそうですが、lintが効いていると思います。