Flutterでアプリ開発する場合、状態管理をRiverpoodで実装するケースは多いと思います。本記事は、Riverpodの状態のライフサイクルについて纏めました。
Riverpod とは?
Riverpod は、Flutter における状態管理の課題を解決するために設計されたフレームワークです。Provider の欠点を克服し、より型安全で保守性の高いコードを書くことができます。
Provider のライフサイクル
Riverpod における Provider のライフサイクルを理解することは、効率的な状態管理を行う上で非常に重要です。以下に、Provider のライフサイクルの主要なフェーズを示します。
1. 作成 (Creation)
Provider は、最初に定義されたとき「作成」されますが、実際にインスタンス化されるのは最初に使用されるときです。
final counterProvider = StateProvider<int>((ref) {
print('CounterProvider が初期化されました');
return 0;
});
2. 初期化 (Initialization)
Provider が最初に読み取られたとき、つまり .watch()
や .read()
メソッドが呼び出されたときに初期化されます。この時点で Provider の値が計算され、メモリに保持されます。
3. 依存関係の追跡 (Dependency Tracking)
Riverpod の強力な機能の一つは、Provider 間の依存関係を自動的に追跡することです。あるプロバイダーが別のプロバイダーを監視(watch)すると、依存関係が確立されます。
final doubledCounterProvider = Provider<int>((ref) {
// counterProvider への依存関係を確立
final count = ref.watch(counterProvider);
return count * 2;
});
4. 更新 (Update)
Provider の値が変更されると、その Provider を監視しているすべての依存関係に通知が送られ、再評価されます。
5. 破棄 (Disposal)
Provider が不要になると、そのリソースを解放するために破棄されます。これには主に二つのケースがあります:
-
.autoDispose
モディファイアを使用していて、Provider に監視者がいなくなった場合 -
ref.onDispose()
コールバックが実行される場合(例:ProviderScope が破棄されるとき)
final timerProvider = StreamProvider.autoDispose<int>((ref) {
final controller = StreamController<int>();
// タイマーのセットアップ
final timer = Timer.periodic(Duration(seconds: 1), (timer) {
controller.add(timer.tick);
});
// 破棄時にリソースをクリーンアップ
ref.onDispose(() {
print('timerProvider が破棄されました');
timer.cancel();
controller.close();
});
return controller.stream;
});
autoDispose モディファイアの影響
autoDispose
モディファイアを使用すると、Provider のライフサイクルが変わります。このモディファイアを使用した Provider は、監視者がいなくなると自動的に破棄されます。
final weatherProvider = FutureProvider.autoDispose<Weather>((ref) {
print('WeatherProvider が作成されました');
// キャッシュしたい場合は、Provider の破棄を防ぐことができます
ref.keepAlive();
ref.onDispose(() {
print('WeatherProvider が破棄されました');
});
return fetchWeather();
});
依存関係のライフサイクルと再評価
Riverpod では、Provider の依存関係が変更されると、その Provider も再評価されます。
ProviderScope と Provider のライフサイクル
Riverpod の各 Provider は、ProviderScope
ウィジェット内で管理されます。ProviderScope
が破棄されると、その配下のすべての Provider も破棄されます。
void main() {
runApp(
// アプリケーション全体のProviderScope
ProviderScope(
child: MyApp(),
),
);
}
// ネストされたProviderScopeの例
Widget build(BuildContext context) {
return ProviderScope(
// このスコープ内のProviderはこのウィジェットが破棄されると
// すべて破棄されます
overrides: [
// 特定のProviderをオーバーライドすることも可能
counterProvider.overrideWithValue(StateController(10)),
],
child: SomeWidget(),
);
}
ref.listenのライフサイクル
ref.listen
メソッドを使用すると、Provider の状態変化を監視し、その変化に反応することができます。
ref.listen<int>(counterProvider, (previous, next) {
if (previous != null && next > previous) {
print('カウンターが増加しました: $previous -> $next');
}
});
このリスナーは、ref
オブジェクトが属する Provider または Widget が破棄されるまで有効です。
まとめ
Riverpod の Provider は以下のライフサイクルを持ちます:
- 作成 - Provider が定義されるとき
- 初期化 - Provider が最初に使用されるとき
- 依存関係の追跡 - Provider が他の Provider を監視するとき
- 更新 - Provider の値が変更されるとき
- 破棄 - Provider が不要になり、リソースが解放されるとき
Riverpod はこのライフサイクルを通じて、効率的なメモリ管理と状態管理を実現します。autoDispose
モディファイアやオーバーライドの仕組みを活用することで、さらに柔軟な状態管理が可能になります。
適切にライフサイクルを理解し、活用することで、パフォーマンスが高く、メンテナンスしやすいFlutterアプリケーションを構築することができます。