Riverpod GeneratorでのProvider自動生成をまとめてみた

Riverpod GeneratorでのProvider自動生成をまとめてみた

Clock Icon2024.12.13

こんにちは、ゲームソリューション部のsoraです。
今回は、Riverpod GeneratorでのProvider自動生成をまとめてみたことについて書いていきます。

利用するパッケージ

flutter_riverpod
Riverpodを使用するために必要
https://pub.dev/packages/flutter_riverpod

riverpod_annotation
Riverpod Generatorのアノテーション@riverpodを使用するために必要
https://pub.dev/packages/riverpod_annotation

riverpod_generator
Riverpod Generatorを使用するために必要
https://pub.dev/packages/riverpod_generator

build_runner
Dartにてコード生成を行う際に使用するパッケージ
コードの自動生成を行うために必要
https://pub.dev/packages/build_runner

共通部分のコード

必要なパッケージをpubspec.yamlに追記します。
関係する部分を一部抜粋して記載しています。

pubspec.yaml
# (一部抜粋)
dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.6.1
  riverpod_annotation:
  http:

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^3.0.0
  riverpod_generator:
  build_runner:

Riverpod Generatorで生成したProviderを使用して表示するテスト用の画面も作るため、main.dartも以下に記載しておきます。
フォントは好みで入れているので、不要であれば削除してください。

main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'contents.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.lightBlueAccent),
          useMaterial3: true,
          fontFamily: 'Noto Sans JP'
      ),
      home: ContentsPage(),
    );
  }
}

Riverpod Generatorでのコード生成の実践

ここからはRiverpodで使用される5つのProviderに対して、それぞれRiverpod Generatorを使用してコード生成を行い、画面表示までやっていきます。
コード生成するためのコードを作成した後、以下コマンドを実行することで生成されます。

# ビルド
flutter pub run build_runner build
# 既存の生成ファイルを削除してからビルド
flutter pub run build_runner build --delete-conflicting-outputs

Provider

以下がProvider生成用のコードです。
BMIを計算するコードにしています。

bmi_calculator.dart
// @riverpodを使用するために利用
import 'package:riverpod_annotation/riverpod_annotation.dart';

// コード生成に作成されるファイルの指定「part '[ファイル名].g.dart'」
part 'bmi_calculator.g.dart';


double bmiCalculator(BmiCalculatorRef ref, double height, double weight) {
  return weight / ((height / 100) * (height / 100));
}

動作確認用の画面を表示するため、以下に生成したProviderを使って値を表示します。

画面表示用のコード
contents.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'provider/bmi_calculator.dart';

class ContentsPage extends ConsumerWidget {
  ContentsPage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    // 使用例
    final bmi = ref.watch(bmiCalculatorProvider(170, 60));

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Provider自動生成テスト'),
      ),
      body: Center(
        child: Text(
          'BMI: ${bmi.toStringAsFixed(2)}',
          style: const TextStyle(fontSize: 30),
        )
      )
    );
  }
}

以下のように表示することができました。
riverpod-generator-01

FutureProvider

以下がFutureProvider生成用のコードです。
APIから情報を取得するコードにしています。

Providerと書き方は同じで、戻り値の型がFutureに変わっているだけです。
(非同期処理でasyncawaitなどがあったりはしますが)

user_data.dart
import 'dart:convert';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:http/http.dart' as http;

part 'user_data.g.dart';


Future<String> userData(UserDataRef ref) async {
  final response = await http.get(Uri.https('reqres.in', '/api/users/2'));
  final jsonResponse = jsonDecode(response.body);
  String firstName = jsonResponse['data']['first_name'];
  return firstName;
}

動作確認用の画面を表示するため、以下に生成したFutureProviderを使って値を表示します。

画面表示用のコード
contents.dart
import 'package:blog_test2/provider/user_data.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class ContentsPage extends ConsumerWidget {
  ContentsPage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final name = ref.watch(userDataProvider);

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('FutureProvider自動生成テスト'),
      ),
      body: Center(
        child: name.when(
          data: (name) => Text(
            '名前: $name',
            style: const TextStyle(fontSize: 30),
          ),
          loading: () => const CircularProgressIndicator(),
          error: (error, stack) => Text(
            'エラー: $error',
            style: const TextStyle(
              fontSize: 30,
              color: Colors.red,
            ),
          ),
        ),
      )
    );
  }
}

以下のように表示することができました。
riverpod-generator-02

StreamProvider

以下がStreamProvider生成用のコードです。
5秒のカウントダウンタイマーにしています。

こちらもProviderと書き方は同じで、戻り値の型がStreamに変わっているだけです。

countdown.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'countdown.g.dart';


Stream<int> countDown(CountDownRef ref) {
  return Stream.periodic(
    const Duration(seconds: 1),
        (count) => 5 - count,
  ).take(6);
}

動作確認用の画面を表示するため、以下に生成したStreamProviderを使って値を表示します。

画面表示用のコード
contents.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'provider/countdown.dart';

class ContentsPage extends ConsumerWidget {
  ContentsPage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final countdown = ref.watch(countDownProvider);

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('StreamProvider自動生成テスト'),
      ),
      body: Center(
        child: countdown.when(
          data: (count) => Text(
            count.toString(),
            style: const TextStyle(fontSize: 40),
          ),
          loading: () => const CircularProgressIndicator(),
          error: (error, stack) => Text('Error: $error'),
        )
      )
    );
  }
}

以下のように表示することができました。
riverpod-generator-03

NotifierProvider

以下がNotifierProvider生成用のコードです。
Flutterのプロジェクト作成時に作られるようなカウンターアプリにしています。

Notifierクラスを定義するため、classになっています。

counter.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'counter.g.dart';


// 基底クラス _$[クラス名]
class Counter extends _$Counter {
  
  int build() {
    return 0;
  }
  void increase() {
    state++;
  }
}

動作確認用の画面を表示するため、以下に生成したNotifierProviderを使って値を表示します。

画面表示用のコード
contents.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'provider/counter.dart';

class ContentsPage extends ConsumerWidget {
  ContentsPage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final counter = ref.watch(counterProvider);
    final counterNotifier = ref.read(counterProvider.notifier);

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('NotifierProvider自動生成テスト'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "$counter",
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            ElevatedButton(
              child: const Text('Count Up'),
              onPressed: (){
                counterNotifier.increase();
              },
            ),
          ],
        ),
      )
    );
  }
}

以下のように表示することができました。
riverpod-generator-04

AsyncNotifierProvider

以下がAsyncNotifierProvider生成用のコードです。
APIからデータを取得したり、状態を初期状態に戻したりしています。

基本的にはNotifierProviderと書き方は同じです。

user_data_operation.dart
import 'dart:convert';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:http/http.dart' as http;

part 'user_data_operation.g.dart';


class UserDataOperation extends _$UserDataOperation {
  
  FutureOr<String> build() async {
    // buildメソッドでラップしているため、AsyncValue.data('')としなくて良い
    return '';
  }

  // データを取得するメソッド
  Future<void> fetchUserData() async {
    final response = await http.get(Uri.https('reqres.in', '/api/users/2'));
    final jsonResponse = jsonDecode(response.body);
    String firstName = jsonResponse['data']['first_name'];
    state = AsyncValue.data(firstName);
  }

  // データをクリアするメソッド
  void clearData() {
    state = AsyncValue.data('');
  }
}

動作確認用の画面を表示するため、以下に生成したAsyncNotifierProviderを使って値を表示します。

画面表示用のコード
contents.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'provider/user_data_operation.dart';

class ContentsPage extends ConsumerWidget {
  ContentsPage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final name = ref.watch(userDataOperationProvider);
    final nameNotifier = ref.read(userDataOperationProvider.notifier);

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('AsyncNotifierProvider自動生成テスト'),
      ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              name.when(
                data: (name) => Text(
                  '名前: $name',
                  style: const TextStyle(fontSize: 30),
                ),
                loading: () => const CircularProgressIndicator(),
                error: (error, stack) => Text(
                  'エラー: $error',
                  style: const TextStyle(
                    fontSize: 30,
                    color: Colors.red,
                  ),
                ),
              ),
              const SizedBox(height: 20),
              ElevatedButton(
                child: const Text('get data'),
                onPressed: (){
                  nameNotifier.fetchUserData();
                },
              ),
              const SizedBox(height: 10),
              ElevatedButton(
                child: const Text('clear data'),
                onPressed: (){
                  nameNotifier.clearData();
                },
              ),
            ],
          )
        )
    );
  }
}

以下のように表示することができました。
riverpod-generator-05

参考

無料部分を参考にしました。
https://zenn.dev/riscait/books/flutter-riverpod-practical-introduction/viewer/riverpod-generator

最後に

今回は、Riverpod GeneratorでのProvider自動生成をまとめてみたことを記事にしました。
どなたかの参考になると幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.