【Flutter/Dart】Riverpodの使い方!状態管理の基本

この記事からわかること
- Flutter/Dartで状態管理を行うRiverpodとは?
- StateProvider、StateNotifier、ProviderScope、ConsumerWidgetの使い方
index
[open]
\ アプリをリリースしました /
環境
- Android Studio:Meerkat
- Xcode:16.0
- Flutter:3.29.2
- Dart:3.7.2
- Riverpod:2.6.1
- Mac M1:Sequoia 15.4
Riverpodとは?
公式リファレンス:https://riverpod.dev/
「Riverpod」とはFlutterアプリで状態管理を行うためのパッケージです。従来人気だった「Provider」という状態管理のパッケージと同じ開発者が開発しており、ProviderのアナグラムでRiverpodと名付けられたようです。
「Riverpod」を使用するメリットは「無駄な再描画(リビルド)を減らしパフォーマンスを向上できる」ことです。これはProviderでも同じ役割を果たすことが可能ですが、Widgetツリー(BuildContext)に依存してしまうデメリットがありました。これを解消しているのがRiverpodでProviderの上位互換的な位置付けになっています。
RiverpodとProviderの違い
「Provider」との違いは以下の通りです。
項目 | Provider | Riverpod |
---|---|---|
状態の管理 | Widgetツリーに依存 | グローバルに定義可能(ツリーに依存しない) |
BuildContext 依存 | 必須(watchやreadに必要) | 不要(refで完結) |
リスナーの扱い | ChangeNotifierなどで明示的に実装 | ref.watchで自動的に検知 |
再利用性 | 低め(Widgetツリー依存) | 高い(他の層やテストで使いやすい) |
グローバル状態管理 | やや面倒(contextが必要) | 簡単(refで直接アクセス可能) |
テストのしやすさ | 難しい(Widgetテストが必要) | ProviderContainerで簡単にテスト可能 |
パッケージの種類
Riverpodに関するパッケージは3つ存在します。それぞれ使い道が異なりますがFlutterでiOS/Androidアプリ開発を行いたい場合はflutter_riverpod
を使用すればOKです。
- riverpod・・・Dart向け
- flutter_riverpod・・・Flutter向け
- hooks_riverpod・・・Flutter + flutter_hooks
導入
Flutterでflutter_riverpodを利用できるようにするために以下のコマンドを実行してパッケージを導入します。
これでパッケージの導入が完了し、import
文を追加すれば使用できるようになります。
Providerの定義
Riverpodでも「Provider」という概念があります。これはProvider
パッケージと基本的には同じ概念で値を提供するための仕組みを構築するためのものになります。Provider
パッケージでは「状態を管理する機能」と「値を提供する機能(Provider)」が別のレイヤーとして管理されていました。
Riverpod
パッケージでは状態管理とProviderが1セットとして定義することが可能です。
また別に定義することも可能ではあります。
カウンターアプリで使い方を学ぼう
Riverpodの実装方法を理解するために簡単なカウンターアプリを作成してみます。その前に重要になるポイントを確認しておきます。それぞれの詳細は後述するとして全体のコードを確認してみましょう。
3つのポイント
- StateProvider・・・状態を直接持つProvider
- ProviderScope・・・Providerを使うための「スコープ」・状態の提供元
- ConsumerWidget・・・状態の変化を観測し再描画
状態を直接持つProvider
まずは状態を管理 & 値の変化を通知するProviderを定義します。StateProvider
では単一の値を保持します。
続いてProviderを使用できるようにするためにProviderScope
で囲みます。これはmain
メソッド内のrunApp
で囲んでおけばOKです。
最後に更新対象となるWidgetの継承をConsumerWidget
にします。StatelessWidget
を拡張したクラスでWidgetRef
型の参照を取得することができ、ここからProviderの値を「監視(watch)」・「読み取り(read)」できるようになります。
Consumer系の種類
Riverpodでは登録したProviderへアクセスするためにWidgetRef
型を介する必要があります。WidgetRef
型を参照できるようにするためのConsumer系のクラスは色々あるので以下の記事を参考にしてください。
Providerの種類
Riverpodから提供されている「Provider」にはいくつか種類があります。
具体的なクラス | 使い所 |
---|---|
StateProvider | 単一の値を管理したい場合 |
StateNotifierProvider | 状態管理にロジックを含ませたい場合 |
Provider | 結果をキャッシュさせたい場合 |
FutureProvider | 非同期値(Future)の提供 |
StreamProvider | リアルタイムなStreamデータの提供 |
StateProvider
StateProvider
は単一の値を管理するためのProviderです。 後述するStateNotifierProvider
の簡易版になっており、データに変化があった場合に変更されたことを通知します。
扱えるデータ型
- プリミティブ型(String / int / bool etc...)
- コレクション型(List / Map)
- クラス(オブジェクト)型
- 列挙(Enum)型
- 非同期(Future / Stream)型
単一のデータ型であれば扱うことができるので状態管理に複雑な処理が絡まないのであればStateProvider
を使用するのが一番シンプルです。
StateNotifierProvider
公式リファレンス:StateNotifierProvider
StateNotifierProvider
はStateNotifierクラスを監視し公開するためのProviderです。StateNotifier
を継承したクラスに対して使用できるので状態管理だけでなく、ロジックを持たせたい場合に活用できます。
Provider
Provider
は状態ではなく結果を保持するための読み取り専用のProviderです。今までは状態自体を保持し変化を外部へ通知することが役割でした。Provider
を使用する場合としない場合をみながら使い方を考えてみます。今回は先ほどのカウンターの「デクリメントボタンに0の場合に非活性になる機能」を実装してみます。まずはProviderを使用しない場合です。
機能的には問題はなく動作しますがパフォーマンス的に問題があります。それはcounterProvider
自体をwatch
しているためカウンターの値が変化するたびにデクリメントボタン自体が都度再描画されてしまう事です。本来なら活性 / 非活性が切り替わるタイミングの時のみ再描画されることが理想です。これがProvider
を使用することで実現できます。「インクリメント可能かどうかを計算するプロバイダ」を作成し、カウンターの観測結果のみを公開するようにすることでカウンターの値の変化での再描画ではなく、インクリメント可能かどうかの結果の変化で再描画されるようにできました。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。