Flutter

Riverpod NotifierProviderの使い方とStateNotifierProviderとの違い【Riverpod 3.0対応】

RiverpodでUIの状態を持たせるとき、いまの標準は NotifierProvider です。かつて主役だった StateNotifierProvider は、2025年リリースの Riverpod 3.0(最新は3.3.2、2026年6月10日リリース)でレガシー扱いになりました。本記事は、NotifierProvider の書き方(build()・プロバイダ宣言・状態更新)を具体コードで示し、StateNotifierProvider との違いを表で整理したうえで、新規開発と既存コードの移行でどちらを選ぶかまで解説します。非同期を扱う AsyncNotifierProvider とコード生成(@riverpod)にも触れます。

まとめ:NotifierProviderとStateNotifierProviderの結論

  • 新規開発は NotifierProvider(+非同期は AsyncNotifierProvider)で書く。StateNotifierProvider は Riverpod 3.0 でレガシー化し、非推奨になった。
  • 最大の違いは初期化と ref の扱い。NotifierProvider は初期化を build() に集約し ref を this.ref で参照する。StateNotifierProvider はコンストラクタで初期値を設定し ref を引数で受け取る。
  • 既存の StateNotifier は 3.0 でも動くが、package:flutter_riverpod/legacy.dart の import が要る。テストを書いてから段階的に Notifier へ移す。
  • コード生成(@riverpod)を使うと、NotifierProvider/AsyncNotifierProvider の宣言を自動生成でき、記述量が減る。

NotifierProviderの位置づけ(Riverpodの現行標準API)

NotifierProvider は、Notifier クラスが持つ状態をウィジェットに公開するためのプロバイダです。Riverpod 2.0 で導入され、3.0 で状態管理の中心になりました。旧来の StateNotifier が外部の state_notifier パッケージに依存していたのに対し、NotifierAsyncNotifier は Riverpod 本体に組み込まれています。追加パッケージなしで使え、後述するコード生成とも一体で設計されている点が現行APIとしての位置づけです。

扱う状態が同期値(カウンタ、フォーム入力、フィルタ条件など)なら NotifierNotifierProvider、API取得のように非同期でロード・エラーを持つなら AsyncNotifierAsyncNotifierProvider を選びます。

NotifierProviderの基本的な使い方

Notifierクラスとbuild()で初期状態を返す

Notifier<T> を継承し、build() の戻り値が初期状態になります。状態の更新は state への代入で行い、他プロバイダの参照は this.ref(メソッド内では ref)から取得します。コンストラクタは基本的に空で構いません。

import 'package:flutter_riverpod/flutter_riverpod.dart';

// build() が初期値を返す。初期化ロジックはすべてここに集約する
class Counter extends Notifier<int> {
  @override
  int build() => 0;

  void increment() => state++;
  void reset() => state = 0;
}

// プロバイダの宣言。コンストラクタ参照 Counter.new を渡す
final counterProvider = NotifierProvider<Counter, int>(Counter.new);

NotifierProviderの宣言とウィジェットからの参照

状態の読み取りは ref.watch(counterProvider)、メソッド呼び出しは ref.read(counterProvider.notifier) 経由です。.notifier を付けると状態値ではなく Notifier インスタンスが得られ、そこから increment() などを呼びます。

class CounterView extends ConsumerWidget {
  const CounterView({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Column(
      children: [
        Text('$count'),
        ElevatedButton(
          onPressed: () => ref.read(counterProvider.notifier).increment(),
          child: const Text('増やす'),
        ),
      ],
    );
  }
}

StateNotifierProviderの現状(Riverpod 3.0でレガシー化)

StateNotifierProvider は、StateNotifier クラスの状態を公開するプロバイダで、Riverpod 2.x まで標準的に使われてきました。StateNotifier はコンストラクタで super(初期値) を呼んで初期状態を設定し、ref が必要な場合はコンストラクタ引数で受け取る設計です。

// Riverpod 3.0 では legacy.dart からの import が必要
import 'package:flutter_riverpod/legacy.dart';

// 初期値はコンストラクタの super() で設定する(レガシー)
class CounterOld extends StateNotifier<int> {
  CounterOld() : super(0);

  void increment() => state++;
}

final counterOldProvider =
    StateNotifierProvider<CounterOld, int>((ref) => CounterOld());

Riverpod 3.0 では、StateNotifierProvider・StateProvider・ChangeNotifierProvider はコアAPIから外れ、legacy.dart という別 import に移されました。削除はされていないため既存コードは動きますが、公式に非推奨(not recommended)とされています。「flutter stateprovider」で探しているケースも同様で、新規に StateProvider を採用する理由は現在ほとんどありません。

NotifierProviderとStateNotifierProviderの違い

両者は「状態を持つクラスをプロバイダで公開する」点は同じですが、初期化の場所・ref の受け取り方・パッケージ依存・3.0での位置づけが異なります。状態更新が state = 値 で同じなので混同されがちですが、設計思想が違います。

観点 NotifierProvider(現行) StateNotifierProvider(レガシー)
初期状態の設定 build() の戻り値 コンストラクタの super(初期値)
初期化ロジック build() に集約 コンストラクタとプロバイダに分散
refの取得 this.ref(組み込み) コンストラクタ引数で受け渡し
パッケージ依存 Riverpod本体に組み込み state_notifier 由来
必要なimport 通常の flutter_riverpod.dart legacy.dart
コード生成(@riverpod) 対応 非対応
Riverpod 3.0での扱い 推奨(標準) 非推奨(レガシー)

実務上の要点は、初期化とリアクティブな依存の宣言が build() の一箇所にまとまることです。StateNotifier ではコンストラクタとプロバイダ定義に処理が分かれ、依存の再取得タイミングが読みにくくなりがちでした。build() 集約により、依存プロバイダが変化したときの再構築が把握しやすくなります。

NotifierProviderとStateNotifierProviderの使い分け

判断はシンプルです。新規のコードは NotifierProvider(非同期は AsyncNotifierProvider)で書く。StateNotifierProvider を新しく選ぶ理由は、3.0 時点では基本的にありません。

既存プロジェクトに StateNotifier が多数ある場合は、3.0 に上げても legacy.dart を import すればそのまま動くため、一度に全部を書き換える必要はありません。移行するときは、対象の StateNotifier に対して期待する挙動のテストを先に書き、コンストラクタの super(初期値)build() の戻り値へ、コンストラクタ引数の ref を this.ref へ移し、テストが通ることを確認しながら1クラスずつ置き換えます。UI側は watchread の呼び出しがほぼそのまま使えるため、変更は Notifier クラスとプロバイダ宣言に集中します。この段取りは、アプリ全体の設計と併せて計画すると安全です(FlutterでのMVVMパターンの実装例とRiverpodの利用法も参考になります)。

AsyncNotifierProviderによる非同期状態の実装例

API取得やDBアクセスのように、ローディング・成功・エラーを扱う状態には AsyncNotifierAsyncNotifierProvider を使います。build()FutureOr<T> を返し、state は自動的に AsyncValue<T> になります。非同期処理は AsyncValue.guard() で包むと、例外を AsyncError として自動的に状態へ反映できます。

import 'package:flutter_riverpod/flutter_riverpod.dart';

class ArticlesNotifier extends AsyncNotifier<List<Article>> {
  @override
  Future<List<Article>> build() async {
    // build() の戻り値がそのまま初期状態(AsyncData)になる
    return ref.read(articleRepositoryProvider).fetchAll();
  }

  Future<void> reload() async {
    state = const AsyncLoading();
    // guard が例外を AsyncError に変換して state へ反映する
    state = await AsyncValue.guard(
      () => ref.read(articleRepositoryProvider).fetchAll(),
    );
  }
}

final articlesProvider =
    AsyncNotifierProvider<ArticlesNotifier, List<Article>>(ArticlesNotifier.new);

UI側は ref.watch(articlesProvider) で受け取った AsyncValue.when(data:, loading:, error:) で分岐すれば、ローディング表示とエラー表示を宣言的に書けます。NotifierProvider と AsyncNotifierProvider の違いは「同期値を持つか、AsyncValue でロード・エラーを持つか」で、扱う状態の性質で選び分けます。

コード生成(@riverpod)とRiverpod 3.0の新機能

手書きの NotifierProvider<...>(...) 宣言は、コード生成に置き換えられます。riverpod_generator を入れてクラスに @riverpod を付けると、対応するプロバイダが自動生成され、宣言の重複と型引数の記述ミスが減ります。

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'counter.g.dart';

@riverpod
class Counter extends _$Counter {
  @override
  int build() => 0;

  void increment() => state++;
}
// ビルドすると counterProvider(AutoDispose版)が自動生成される

Riverpod 3.0(2025年リリース、最新3.3.2=2026年6月10日)では、失敗したプロバイダを指数バックオフ(初回200ミリ秒から最大6.4秒まで倍々)で自動リトライする機能、非同期後にプロバイダが生存しているかを確認する Ref.mounted、オフライン永続化やMutations(いずれも実験的)といった機能が加わりました。コード生成の詳しい導入手順や 3.0 の変更点は、Riverpodとは何か?Flutterの状態管理ライブラリとしての特徴や他ライブラリとの違いを徹底解説で扱っています。バージョンや実験的機能の扱いは変わりやすいため、最新は公式ドキュメントで確認してください。

よくある質問

NotifierProviderとStateNotifierProviderはどちらを使うべきですか?

新規コードは NotifierProvider を使ってください。StateNotifierProvider は Riverpod 3.0 でレガシー化し非推奨になったため、新しく採用する理由はほぼありません。既存の StateNotifier は動かし続けられますが、いずれ Notifier へ移行する前提で扱うのが安全です。

StateNotifierProviderはもう使えないのですか?

使えます。3.0 でも削除はされておらず、package:flutter_riverpod/legacy.dart を import すれば従来どおり動作します。ただし公式に非推奨とされているため、長期的には Notifier への移行を計画してください。

NotifierProviderとAsyncNotifierProviderの違いは何ですか?

NotifierProvider は同期的な状態(build() が値を返す)、AsyncNotifierProvider は非同期状態(build()Future を返し、stateAsyncValue になる)を扱います。API取得やDBアクセスのようにローディング・エラーを持つ状態は AsyncNotifierProvider を選びます。

flutter stateprovider はどう扱えばよいですか?

StateProvider も 3.0 でレガシー化し legacy.dart へ移動しました。単純な値の保持であっても、新規では Notifier+NotifierProvider に置き換えるのが推奨です。

コード生成(@riverpod)は必須ですか?

必須ではありません。手書きの NotifierProvider 宣言でも同じことができます。ただしコード生成を使うと宣言の重複や型引数のミスが減るため、規模が大きいプロジェクトでは導入する価値があります。

関連記事

資料請求

RELATED POSTS 関連記事