Riverpod GeneratorでのProvider自動生成をまとめてみた
こんにちは、ゲームソリューション部のsoraです。
今回は、Riverpod GeneratorでのProvider自動生成をまとめてみたことについて書いていきます。
利用するパッケージ
flutter_riverpod
Riverpodを使用するために必要
riverpod_annotation
Riverpod Generatorのアノテーション@riverpod
を使用するために必要
riverpod_generator
Riverpod Generatorを使用するために必要
build_runner
Dartにてコード生成を行う際に使用するパッケージ
コードの自動生成を行うために必要
共通部分のコード
必要なパッケージを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
も以下に記載しておきます。
フォントは好みで入れているので、不要であれば削除してください。
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を計算するコードにしています。
// @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を使って値を表示します。
画面表示用のコード
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),
)
)
);
}
}
以下のように表示することができました。
FutureProvider
以下がFutureProvider生成用のコードです。
APIから情報を取得するコードにしています。
Providerと書き方は同じで、戻り値の型がFutureに変わっているだけです。
(非同期処理でasync
やawait
などがあったりはしますが)
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を使って値を表示します。
画面表示用のコード
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,
),
),
),
)
);
}
}
以下のように表示することができました。
StreamProvider
以下がStreamProvider生成用のコードです。
5秒のカウントダウンタイマーにしています。
こちらもProviderと書き方は同じで、戻り値の型がStreamに変わっているだけです。
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を使って値を表示します。
画面表示用のコード
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'),
)
)
);
}
}
以下のように表示することができました。
NotifierProvider
以下がNotifierProvider生成用のコードです。
Flutterのプロジェクト作成時に作られるようなカウンターアプリにしています。
Notifierクラスを定義するため、classになっています。
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'counter.g.dart';
// 基底クラス _$[クラス名]
class Counter extends _$Counter {
int build() {
return 0;
}
void increase() {
state++;
}
}
動作確認用の画面を表示するため、以下に生成したNotifierProviderを使って値を表示します。
画面表示用のコード
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();
},
),
],
),
)
);
}
}
以下のように表示することができました。
AsyncNotifierProvider
以下がAsyncNotifierProvider生成用のコードです。
APIからデータを取得したり、状態を初期状態に戻したりしています。
基本的にはNotifierProviderと書き方は同じです。
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を使って値を表示します。
画面表示用のコード
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でのProvider自動生成をまとめてみたことを記事にしました。
どなたかの参考になると幸いです。