Riverpod のメジャーバージョンが2へと上がり、新機能が追加されて、よりシンプルかつパワフルなコーディングが可能となりました。しかしながら、Riverpod はプロバイダが中心的な役割を果たしますが、それらは種類が多く、適宜使い分けることが依然と難しい状況です。そこでこの記事では、バージョン2におけるプロバイダごとの違いを整理します。
8種類のプロバイダ
RIverpod 2.x では新たに2つのプロバイダが追加され、合計8種類が用意されています。新たに加わった2種類は、従来からある3種類を置き換えることができるため、実質的には5種類のプロバイダを使い分けることになるでしょう。
Provider
StateProvider
(レガシー)StateNotifierProvider
(レガシー)FuturProvider
StreamProvider
ChangeNotifierProvider
(レガシー)NotifierProvider
(RIverpod 2.0 で追加)AsyncNotifierProvider
(RIverpod 2.0 で追加)
Provider
Provider
は基本のプロバイダです。
特徴
- 同期的に値を生成する
- 変更不可能な状態を持つ
用途
- 同期計算結果のキャッシュ
- (リポジトリやHTTPクライアントのような)変更されない依存関係の供給
例:リポジトリの供給
import 'package:http/http.dart' as http;
import 'package:flutter_riverpod/flutter_riverpod.dart';
// Weather, OpenWeatherMapAPI のインポートは省略
abstract class WeatherRepository {
Future<Weather> getWeather({required String city});
}
final weatherRepositoryProvider = Provider<WeatherRepository>((ref) {
return HttpWeatherRepository(api: OpenWeatherMapAPI(), client: http.Client());
});
WeatherRepository
コントラクトを実装した HttpWeatherRepository
は、変更可能な状態を持っていないため、Provider
で供給するのに適しています。
補足として、このようなケースでの Riverpod 以外の方法の選択肢としては、get_it
によるサービスロケータや flutter_bloc
ライブラリが挙げられます。
FutureProvider
FutureProvider
は非同期操作が可能な Provider
です。
特徴
- 非同期に値を生成
AsyncValue
による3つの状態——ローディング状態 (loading
)、エラー状態 (error
)、ロード完了状態 (data
) ——ごとに処理を分けることができる
用途
- 非同期計算結果のキャッシュ
- 非同期操作の過程や結果に応じた処理
例:非同期で取得される天気データの供給
FutureProvider
の定義側:
// 天気データリポジトリプロバイダ
final weatherRepositoryProvider = Provider<WeatherRepository>((ref) {
return WeatherRepository();
});
final weatherFutureProvider = FutureProvider.autoDispose<Weather>((ref) {
// プロバイダからリポジトリを取得
final weatherRepository = ref.watch(weatherRepositoryProvider);
// Future<Weather> を返すメソッドを呼び出す
return weatherRepository.getWeather(city: 'London');
});
FutureProvider
を利用するウィジェット内部:
Widget build(BuildContext context, WidgetRef ref) {
// FutureProvider を監視し、 AsyncValue<Weather> を取得する
final weatherAsync = ref.watch(weatherFutureProvider);
// パターン マッチングを使用して状態を UI にマッピングする
return weatherAsync.when(
loading: () => const CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
data: (weather) => Text(weather.toString()),
);
}
StreamProvider
StreamProvider
は FutureProvider
のストリーム版です。
特徴
- Riverpod が提供する
AsyncValue
によってloading
/error
状態を処理することができる - ストリームの監視を
ref.watch
で行える - ストリームの直近の値をキャッシュする
用途
- (Firebase や WebSocket といった)
Stream
形式のデータ供給の監視 - 一定時間ごとの別のプロバイダの更新
- テスト時のストリームをモックする
例:Firebase Authentication APIを監視してリアクティブにUIを構築する
StreamProvider
の定義側:
final authStateChangesProvider = StreamProvider.autoDispose<User?>((ref) {
// FirebaseAuth を取得する
final firebaseAuth = ref.watch(firebaseAuthProvider);
// Stream<User?> を返すメソッドを呼び出す
return firebaseAuth.authStateChanges();
});
// FirebaseAuth インスタンスにアクセスするプロバイダ
final firebaseAuthProvider = Provider<FirebaseAuth>((ref) {
return FirebaseAuth.instance;
});
StreamProvider
を利用するウィジェット内部:
Widget build(BuildContext context, WidgetRef ref) {
// StreamProvider を監視して、 AsyncValue<User?> を取得する
final authStateAsync = ref.watch(authStateChangesProvider);
// パターン マッチングを使用して状態を UI にマッピングする
return authStateAsync.when(
data: (user) => user != null ? HomePage() : SignInPage(),
loading: () => const CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
);
}
NotifierProvider
NotifierProvider
は時間の経過とともに変化する可能性のある状態を公開する Notifier
のプロバイダです。
特徴
ref
を渡すことなく読み取りが可能- 複雑な同期的な初期化が可能
StateProvider
を置き換えることができる
用途
- 同期的な
StateProvider
やStateNotifierProvider
のよりスマートな代替え手段
例:カウンター
NotifierProvider
の定義側
import 'package:flutter_riverpod/flutter_riverpod.dart';
class Counter extends Notifier<int> {
@override
int build() {
return 0;
}
void increment() {
state++;
}
}
final counterProvider = NotifierProvider<Counter, int>(() {
return Counter();
});
NotifierProvider
を利用する側
class CounterWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 1. プロバイダーを監視し、値が変更されたら再構築する
final counter = ref.watch(counterProvider);
return ElevatedButton(
// 2. 値を使用する
child: Text('Value: $counter'),
// 3. ボタンコールバック内で状態を変更する
onPressed: () => ref.read(counterProvider.notifier).increment(),
// → StateProvider 風に書くなら、
// ref.read(counterProvider.notifier).state++ としてもよい
);
}
}
AsyncNotifierProvider
AsyncNotifierProvider
は非同期で初期化される NotifierProvider
で、AsyncNotifier
のプロバイダです。但し、autoDispose
を使用する場合は、AsyncNotifier
の替わりに AutoDisposeAsyncNotifier
を利用します。
特徴
ref
を渡すことなく読み取りが可能- 複雑な非同期的な初期化が可能
state
プロパティはAsyncValue
でラップされた形式となる
用途
- 非同期的な
StateProvider
やStateNotifierProvider
のよりスマートな代替え手段
例:認証
AsyncNotifierProvider
の定義側:
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// AsyncNotifier を継承する
class AuthController extends AsyncNotifier<void> {
// build メソッドをオーバーライドして FutureOr を返す
@override
FutureOr<void> build() {
// 値を返すか、void の場合は何もしない
}
Future<void> signInAnonymously() async {
// 自ら ref を読み取ることができる
final authRepository = ref.read(authRepositoryProvider);
// ロード中
state = const AsyncLoading();
// ロード完了
state = await AsyncValue.guard(authRepository.signInAnonymously);
}
}
final authControllerProvider = AsyncNotifierProvider<AuthController, void>(() {
return AuthController();
});
AsyncNotifierProvider
を利用する側:
ref.read(authControllerProvider.notifier).signInAnonymously()