Index
公式の説明によるとこんな感じ。
状態管理のライブラリーであり。以下の特徴がある。
- プログラムのエラーを実行時ではなく、コンパイル時に検出する
- リスニングと結合オブジェクトのネストを削除する
- コードがテスト可能であることを保証する・
同じく状態管理ライブラリーであるproviderの上位互換である
またriverpodはコンセプトは以下の通り
- ウィジェット再構築時に状態を失う心配がなく、安全に状態の作成、監視、破棄が可能
- Flutterのdevtoolで我々のオブジェクトをデフォルトで見えるようにすること
- テスト可能であり、構成可能
- InheritedWidgetsが複数ある場合の可読性を向上させた
- 一方向のデータフローでアプリのスケーラビリティを向上させます。
- オブジェクトの読み込みがコンパイルセーフになり、runtime exceptionがなくなりました。
- 同じ型のproviderを複数持てる
- providerが使用されなくなった時に破棄される
- 計算された状態をもつ
- providerを非公開にする
- 複雑なオブジェクトグラフを簡素化する。非同期状態への依存が容易になる。
- flutterに依存しない
このサイトを参考にする。
3種類のパッケージが存在する。
今回はflutter_riverpodを使用する
以下のコードでインストールする
1 |
flutter pub add flutter_riverpod |
インストールできたら以下のコードを書いて実行してみる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; // 値(ここでは "Hello world")を格納する「プロバイダ」を作成します。 // プロバイダを使うことで値のモックやオーバーライドが可能になります。 final helloWorldProvider = Provider((_) => 'Hello world'); void main() { runApp( // プロバイダをウィジェットで利用するには、アプリ全体を // `ProviderScope` ウィジェットで囲む必要があります。 // ここに各プロバイダのステート(状態)・値が格納されていきます。 ProviderScope( child: MyApp(), ), ); } // StatelessWidget の代わりに Riverpod の ConsumerWidget を継承します。 class MyApp extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final String value = ref.watch(helloWorldProvider); return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('Example')), body: Center( child: Text(value), ), ), ); } } |
hello,worldが表示される。
ProviderはRiverpodにおいて最重要なオブジェクトです。
ある状態をラップするためのオブジェクトであり、監視を可能にしてくれます。
Providerで可能になることは以下のことが可能になります。
- アプリの様々な場所からステートにアクセスできるようになります。 つまり、プロバイダはシングルトンやサービスロケータのようなパターン、依存性注入、あるいは InheritedWidget を完全に代替することができます。
- ステートを別のプロバイダのステートと簡単に組み合わせることができるようになります。 開発では複数のオブジェクトを組み合わせて一つのステートにまとめるのに四苦八苦する場面も多いかと思います。プロバイダにはこのための機能が組み込まれています。
- アプリのパフォーマンスを最適化してくれます。 例えば、ウィジェット更新の条件を限定したり、負荷が高いステートの計算をキャッシュしたりといったことが可能になります。 プロバイダがステートの変化による外部への影響をコントロールしてくれます。
- アプリのテスト容易性を高めてくれます。 プロバイダがあれば
setUp
やtearDown
のような面倒な手順は不要です。 さらに、テスト中のプロバイダの挙動をオーバーライドすることができます。 これにより特異な条件下での動作も確認しやすくなります。 - ロギングやプルリフレッシュ(画面を引っ張って更新)などの高度な機能との組み合わせが容易に実現できます。
グローバル定数として以下のように宣言します。
1 2 3 |
final myProvider = Provider((ref) { return MyValue(); }); |
またflutterアプリのrootにProviderScope()を置く必要がある。
1 2 3 |
void main() { runApp(ProviderScope(child: MyApp())); } |
Providerはいくつか種類があるので、適宜使い分けること
Providerから値を取得するにはrefを使用します。
hello,worldのサンプルだと以下のコードが該当します。
1 |
final String value = ref.watch(helloWorldProvider); |
複数の方法がありますが今回はよく使う2つの方法を解説。
1 2 3 4 5 6 7 8 9 10 |
class HomeView extends ConsumerWidget { const HomeView({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { // `ref` を使ってプロバイダーを監視する final counter = ref.watch(counterProvider); return Text('$counter'); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class HomeView extends ConsumerStatefulWidget { const HomeView({Key? key}) : super(key: key); @override HomeViewState createState() => HomeViewState(); } class HomeViewState extends ConsumerState<HomeView> { @override void initState() { super.initState(); // `ref` は StatefulWidget のすべてのライフサイクルメソッド内で使用可能です。 ref.read(counterProvider); } @override Widget build(BuildContext context) { // `ref` は build メソッド内で使用することもできます。 final counter = ref.watch(counterProvider); return Text('$counter'); } } |
refの使い方は主に以下の3つ
ref.watch
: プロバイダの値を取得した上で、その変化を監視する。値が変化すると、その値に依存するウィジェットやプロバイダの更新が行われる。ref.listen
: プロバイダの値を監視し、値が変化するたびに呼び出されるコールバック関数(画面遷移、ダイアログの表示など)を登録する。ref.read
: プロバイダの値を取得する(監視はしない)。クリックイベント等の発生時に、その時点での値を取得する場合に使用できる。
ref.watchは以下のような感じで使う
1 2 3 4 5 6 7 8 9 10 11 12 13 |
final counterProvider = StateProvider((ref) => 0); class HomeView extends ConsumerWidget { const HomeView({Key? key}): super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { // `ref` を使ってプロバイダを監視する final counter = ref.watch(counterProvider); return Text('$counter'); } } |