そもそもプロバイダってなに?
RiverPodの公式ドキュメントによると、
「プロバイダは Riverpod において中心的な役割を担っています。 プロバイダはあるステート(状態)をラップするためのオブジェクトであり、その監視を可能にしてくれます。」
とあります。要するに、プロバイダはアプリの色々な場所から、ステートの状態にアクセスできるようにしてくれるんです。
プロバイダの利用方法
プロバイダの作成
プロバイダの作成は基本的にグローバル変数として、宣言をしていきます。
final myProvider = Provider((ref) {
return MyValue();
});
- グローバルで宣言します。(プロバイダはイミュータブルなので、心配はないです。)
-
final
で宣言します。 - 右辺の
Provider
で使用するプロバイダの種類を指定しています。他にも、FutureProvider
やStreamProvider
などの様々な種類のプロバイダがあるので、調べてみてください。
プロバイダ修飾子
必要であれば、プロバイダ修飾子をつけてプロバイダを作成してください。
プロバイダ修飾子の種類
-
.autoDispose
: プロバイダの監視が終わったタイミングで自動的にステートを破棄します。 -
.family
: プロバイダ外部の値を用いて、プロバイダを作成できます。
以下、使用例です。
final myAutoDisposeProvider = StateProvider.autoDispose<int>((ref) => 0);
プロバイダの利用
ref
オブジェクトの取得
プロバイダからのref
取得
プロバイダは全てref
オブジェクトを引数とします。
final provider = Provider((ref) {
// `ref` を通じて他のプロバイダを利用する
final repository = ref.watch(repositoryProvider);
return SomeValue(repository);
})
このref
を渡す例は以下の通りです。
final countProvider = StateNotifierProvider<Count, int>((ref) {
return Count(ref);
});
class Counter extends StateNotifier<int> {
Counter(this.ref) : super(0);
final Ref ref;
void increment() {
// Count は `ref` を使って他のプロバイダーを利用することができる
final repository = ref.read(repositoryProvider);
}
}
このようにすることでStateNotifierのCount
はref
を通して、別のプロバイダを利用可能になります。
ウィジェットからのref
取得
通常のウィジェットはref
を取得できないので、Riverpodでは特別なウィジェットを用意しています。
- StatelessWidget → ConsumerWidget
- StatefulWidget (State) → ConsumerStatefulWidget (ConsumerState)
- HookWidget → HookConsumerWidget (flutter_hooksを使用している場合)
ref.watch
プロバイダか、ウィジェットの中で、ref.watch
を用いて、プロバイダを監視することができます。実際のところ、ref.watch
では値の変化に応じて、ウィジェットやプロバイダを変化させます。
以下はウィジェット内で、ref.watch
を使って、プロバイダの値が変わるたびに画面を更新させる処理の例です。
final counterProvider = StateProvider((ref) => 0);
class HomeView extends ConsumerWidget {
const HomeView({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// `ref` を使ってプロバイダを監視する
final counter = ref.watch(counterProvider);
return Text('$counter');
}
}
今回の場合、カウンターの数を値として管理するプロバイダを監視しており、プロバイダの値が変化すると、ウィジェットが更新されて、変化後の値で画面が作られます。
ref.listen
ref.listen
はエラー発生時のモーダル表示などの、変化に応じた処理をするときに使えます。
ref.listen
メソッドは2つの引数を持っています。第一引数はプロバイダ、第二引数はステートが変化したときに実行されるコールバック関数を呼び出した際に、プロバイダの変更前後のステートの値が渡され、それぞれを使用できます。
以下のプロバイダ内で使用した例です。
final counterProvider = StateNotifierProvider<Counter, int>(Counter.new);
final anotherProvider = Provider((ref) {
ref.listen<int>(counterProvider, (int? previousCount, int newCount) {
print('The counter changed $newCount');
});
// ...
});
また、ref.listen
はウィジェットのbuild
メソッド内で使用できます。
final counterProvider = StateNotifierProvider<Counter, int>(Counter.new);
class HomeView extends ConsumerWidget {
const HomeView({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen<int>(counterProvider, (int? previousCount, int newCount) {
print('The counter changed $newCount');
});
return Container();
}
}
ref.read
ref.read
メソッドを使うことでプロバイダの現在のステートを取得できます。ref.read
はユーザの操作によって呼び出される関数内で使用することができる。
final counterProvider = StateNotifierProvider<Counter, int>(Counter.new);
class HomeView extends ConsumerWidget {
const HomeView({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
// `Counter` クラスの `increment()` メソッドを呼び出す
ref.read(counterProvider.notifier).increment();
},
),
);
}
}
このようにプロバイダを用いて、ステータスにアクセスしましょう。
弊社のFlutterアプリでもRiverpodアーキテクチャによく使われるこれらプロバイダを用いて、状態管理をしています。