Flutter Riverpod 3.0 実践改定ガイド:Notifier、AsyncNotifierと自動メモリ解放パターン

Flutter Riverpod 3.0 実践改定ガイド:Notifier、AsyncNotifierと自動メモリ解放パターン
Flutterの主要な状態管理ライブラリである Riverpod が、バージョン3.0への大規模なアーキテクチャ刷新を行いました。旧来の1.x / 2.xで併用されていた StateNotifier や ChangeNotifier などの記法は、クラスベースの Notifier / AsyncNotifier および riverpod_generator によるコード生成 へと完全標準化されました。
なお、@riverpod によるコード生成方式では、Riverpod 2.0 の時点ですでにデフォルトで autoDispose(画面離脱時の自動メモリ解放)動作が有効でした。3.0 で実際に変わったのは、AutoDispose / Family などに分かれていたプロバイダの型インターフェースを Provider / Notifier に統合(unify)し、APIをシンプルにした点です。
本記事では、Riverpod 3.0 の主要な変更点と、Notifier、AsyncNotifier、family パラメータを活用したプロダクションレベルの状態管理実装パターンを解説します。
1. Riverpod 3.0 の主な変更点比較
| 区分 | Riverpod 2.x 以前 (Legacy) | Riverpod 3.0 標準 |
|---|---|---|
| 型インターフェース | Provider / AutoDisposeProvider / FamilyProvider など細分化 |
Provider/Notifier に統合(デフォルト autoDispose 自体は @riverpod コード生成方式で 2.0 から変わらず) |
| 状態管理クラス | StateNotifierProvider / FutureProvider |
Notifier / AsyncNotifier (@riverpod アノテーション) |
| 引数渡し (Family) | .family ラッパーを手動記述 |
build メソッドの引数として自動生成 |
| 非同期状態ハンドリング | AsyncValue.when() による手動分岐 |
拡張された AsyncValue ヘルパーおよびガード処理 |
2. Notifier および AsyncNotifier の実践実装
1) 同期状態管理:Notifier の実装
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'counter_provider.g.dart';
// @riverpod アノテーション使用時はデフォルトで autoDispose が適用されます
@riverpod
class CounterNotifier extends _$CounterNotifier {
@override
int build() {
// 初期状態の返却(画面再進入時に build が再実行されます)
return 0;
}
void increment() {
state = state + 1;
}
void decrement() {
state = state - 1;
}
}
2) 非同期状態管理:AsyncNotifier & Family の実装
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../models/user.dart';
part 'user_provider.g.dart';
// family 引数は build メソッドのパラメータとして直接渡します
@riverpod
class UserDetailNotifier extends _$UserDetailNotifier {
@override
Future<User> build(String userId) async {
// 非同期データの取得
return await ref.read(userRepositoryProvider).fetchUser(userId);
}
Future<void> updateUserName(String newName) async {
// ローディング状態へ遷移
state = const AsyncLoading();
// 状態更新および AsyncValue.guard によるエラーハンドリング
state = await AsyncValue.guard(() async {
final updatedUser = await ref
.read(userRepositoryProvider)
.updateName(arg, newName); // arg は family パラメータ (userId)
return updatedUser;
});
}
}
3. Widget 層での AsyncValue 購読と autoDispose への対応
UI層では ref.watch を使用して状態を購読し、AsyncValue.when によりローディング/エラー/成功時の表示を綺麗に分離します。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class UserProfileScreen extends ConsumerWidget {
final String userId;
const UserProfileScreen({super.key, required this.userId});
@override
Widget build(BuildContext context, WidgetRef ref) {
// family パラメータとともにプロバイダを購読
final userAsync = ref.watch(userDetailNotifierProvider(userId));
return Scaffold(
appBar: AppBar(title: const Text('ユーザープロフィール')),
body: userAsync.when(
data: (user) => Column(
children: [
Text('名前: ${user.name}'),
ElevatedButton(
onPressed: () {
ref
.read(userDetailNotifierProvider(userId).notifier)
.updateUserName('新しい名前');
},
child: const Text('名前変更'),
),
],
),
loading: () => const Center(child: CircularProgressIndicator()),
error: (err, stack) => Center(child: Text('エラーが発生しました: $err')),
),
);
}
}
4. autoDispose に関する実践上の注意点
- グローバル認証・設定状態: アプリのライフサイクル全体で保持すべきプロバイダ(ログイン情報やテーマ設定など)には、明確に
@Riverpod(keepAlive: true)を記述します。 ref.keepAlive()の活用: 非同期処理の実行中に画面を離脱しても一時的に破棄を保留したい場合、build()内でfinal link = ref.keepAlive();を生成し、処理完了後にlink.close()で解放します。- ガビージコレクションのタイミング:
autoDisposeはウィジェットツリーから最後のリスナーが消えても即座には破棄されず、1フレーム分の猶予期間を待ってから、その時点でも未使用であれば状態を破棄します。フォーム入力の一時保存などではキャッシュ戦略に配慮する必要があります。
まとめ
Riverpod 3.0 は、ボイラープレートを最小限に抑え、デフォルトで安全なメモリ管理を提供するモダンな状態管理フレームワークです。
[!NOTE]
- 従来の
StateNotifierからクラスベースのNotifier/AsyncNotifierへ移行しましょう。riverpod_generatorを活用し、familyパラメータおよびプロバイダ生成を自動化します。- デフォルトの
autoDispose動作を理解し、グローバル状態には@Riverpod(keepAlive: true)を明記してメモリリークを防ぎましょう。
新しい Riverpod 3.0 のパターンを採用することで、Flutter アプリの保守性と型安全性を大きく向上させることができます。