今回はRiverpodのプロバイダ「StateNotifierProvider」をFlutterで使用する方法を紹介します。
FlutterでRiverpodを使うのが初めての方は先にこちらの記事をお読みください。

Riverpodの導入
$ flutter pub add flutter_riverpod
$ flutter pub get
pubspec.yamlにパッケージを追加・更新
import 'package:flutter_riverpod/flutter_riverpod.dart';
main.dartファイルにコピペ
「ProviderScope」をFlutterアプリのルートにする
void main() {
runApp(ProviderScope(child: MyApp()));
}
「ProviderScope」をrunApp()でFlutterアプリのルートにします。これでFlutterアプリでRiverpodのプロバイダを使えるようになりました。
「StateNotifierProvider」の使い方
コードで解説してる箇所は記事下のサンプルコードを参考にして読んでみてください。
概要
StateNotifierProviderを使えば、StateNotifierを継承したクラスのステート(値)をプロバイダで渡したり、外部から変更できます。
StateProviderが「列挙型(enum)」「String型」「bool型」「数値型」などシンプルなステートを管理するのに対し、StateNotifierProviderは複雑なロジックが必要となるステートをクラスで管理できます。
文章だけだと分かりにくいのでコードを使いながら説明します。

ステップ1:StateNotifierを継承したクラスを定義
//定義
class Notifier名 extends StateNotifier<型> {
Notifier名() : super(初期値);
void メソッド(){
処理...;
}
...
}
//例
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
//superで指定した値が初期値(stateの初期値は「0」)
void increment() => state++;
void decrement() => state--;
}
StateNotifierProviderを使用するにはステートを管理するクラス(StateNotifierを継承したクラス)が必要となるので先に定義しておきます。
StateNotifierクラスを継承したらステートの「型」「初期値」を指定し、ステートを管理するメソッドを定義します。メソッドでは「state」を使用してステートを変更できます。
ステップ2:StateNotifierProviderを定義
//定義
final プロバイダ名 = StateNotifierProvider<Notifier名, 型>((ref) {
return Notifier名();
});
//例
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
return CounterNotifier();
});
上記コードのようにグローバル定数でプロバイダを定義します。
「ref」をWidgetで使用可能にする
プロバイダの値を取得するには「ref」を使用します。ただ通常のStatelessWidget、StatefulWidgetでは「ref」を使用できないので下記のように置き換える必要があります。
StatelessWidgetでプロバイダを使用する場合
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Container();
}
}
「StatelessWidget」を「ConsumerWidget」に置き換える必要があります。またbuild()の第二引数に「WidgetRef ref」を渡します。Riverpodのコードスニペットプラグインを入れておけば自動生成できます。
StatelessWidgetでプロバイダを使用する場合
class MyApp extends ConsumerStatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
ConsumerState<ConsumerStatefulWidget> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
@override
Widget build(BuildContext context) {
return Container();
}
}
「StatefulWidget と State」を「ConsumerStatefulWidget と ConsumerState」に置き換える必要があります。StatelessWidget同様コードスニペットで自動生成できます。
ref.watchでプロバイダのデータを取得
final counter = ref.watch(counterProvider);
ref.watchを使用してプロバイダ(counterProvider)の現在の値(ステート)を取得します。ref.watchを使用することでプロバイダのステートに変化があった際、常に「counter」に更新された値を反映できます。
ここで押さえておきたいのがcounterは定数なので、counterから直接プロバイダのステートを変更できません。よってプロバイダのステートを変更するには「ref」を使用します。
StateNotifierのメソッドで値を変更
//書き方
ref.read(プロバイダ.notifier).メソッド();
//例
ref.read(counterProvider.notifier).increment();
ref.read(counterProvider.notifier).decrement();
StateNotifierProviderでステート(値)を変更するにはStateNotifierで定義したメソッドを使用します。
サンプルコード
「ref.watch」でcounterProvider(StateNotifierProvider)のステートを取得・監視し、StateNotifierで定義したメソッド「increment()」と「decrement()」でステートの値を変更しています。
//ソース
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
void decrement() => state--;
}
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
return CounterNotifier();
});
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerStatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
ConsumerState<ConsumerStatefulWidget> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
@override
Widget build(BuildContext context) {
final counter = ref.watch(counterProvider);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Riverpod'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
counter.toString(),
style: TextStyle(
fontSize: 50,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
onPressed: () {
ref.read(counterProvider.notifier).increment();
},
icon: Icon(
Icons.add,
size: 50,
),
),
IconButton(
onPressed: () {
ref.read(counterProvider.notifier).decrement();
},
icon: Icon(
Icons.remove,
size: 50,
),
),
],
)
],
),
),
);
}
}
以上です。