JINMUSOFTWARE

Riverpod3入門 Generatorコード生成編

riverpod

Generatorを使用してriverpodのコードを生成してみます。

Riverpod_generatorとは?

Generatorを使用することにより、Riverpodでよくみるボイラーテンプレート少なくします。

build_runnerでコードを生成します。

Dart
final fetchUserProvider = FutureProvider.autoDispose.family<User, int>((
  ref,
  userId,
) async {
  final json = await http.get('api/user/$userId');
  return User.fromJson(json);
});

これがこんな感じで書けます。

Dart
@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

パッケージをインストールします。

Bash
flutter pub add flutter_riverpod
flutter pub add riverpod_annotation
flutter pub add dev:riverpod_generator
flutter pub add dev:build_runner

こっちのほうが早いですね。

Bash
flutter pub add flutter_riverpod riverpod_annotation dev:riverpod_generator dev:build_runner

pubspec.yamlが更新されます。

補足)Riverpod公式の例ではbuild_runnerにはVersionを指定せずに使用していました。

lintを設定します。

  • analysis_options.yaml で設定する
  • riverpod_lint の最新Versionを指定する
analysis_options.yaml
plugins:
  riverpod_lint: 3.1.3

lint設定後に解析が始まります。少し経つと、main.dartにprovider scope が見つからないと警告が発生します。後で修正しましょう。

Build runner の起動

ファイルの変更を監視してコードを生成してくれます。

build_runnerを起動しておきましょう。

Bash
dart run build_runner watch -d

main.dartの編集

さて、lintがうまく設定されていれば、main.dartに警告が発生していると思います。

main.dartを修正しましょう。

importとProviderScopeを追加します。

Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(ProviderScope(child: const MyApp()));
}

Providerの作成

それでは簡単なproviderを定義してみましょう。

hello_world.dartを新規作成します。

コードを記述していくと、part 'hello_world.g.dart'; の部分はエラーになりますが、ファイルが自動生成されればエラーは消えます。

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してみましょう。

Dart
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 を継承してもいいでしょう。

hello_page.dart
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 でも入れてみましょう。

Dart
@riverpod
Future<String> fetchUserName(Ref ref) async {
  // await Future.delayed(Duration(seconds: 1));
  return 'John Doe';
}

NotifierProvider

こちらはカウンターのデモです。

Dart
@riverpod
class Counter extends _$Counter {
  @override
  int build() {
    return 0; // 初期値
  }

  void increment() {
    state++;
  }

  void decrement() {
    state--;
  }

  void reset() {
    state = 0;
  }
}

状態を保持する場合はkeepAlive: true の指定が必要になります。

Dart
@Riverpod(keepAlive: true)
class Counter extends _$Counter {
  // ~
}

AsyncNotifierProvider

Dart
@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が効いていると思います。

参照

Riverpod.dev 公式

About code generation

flutter_riverpod 3.2.1

参考ページ

Riverpod3入門 ProviderとNotifierProvider編

Riverpod3入門 FutureProvider編

Riverpod3入門 AsyncNotifier編