riverpodのライフサイクル

Flutterでアプリ開発する場合、状態管理をRiverpoodで実装するケースは多いと思います。本記事は、Riverpodの状態のライフサイクルについて纏めました。

## Riverpod とは？

Riverpod は、Flutter における状態管理の課題を解決するために設計されたフレームワークです。Provider の欠点を克服し、より型安全で保守性の高いコードを書くことができます。

## Provider のライフサイクル

Riverpod における Provider のライフサイクルを理解することは、効率的な状態管理を行う上で非常に重要です。以下に、Provider のライフサイクルの主要なフェーズを示します。

### 1. 作成 (Creation)

Provider は、最初に定義されたとき「作成」されますが、実際にインスタンス化されるのは最初に使用されるときです。

```dart
final counterProvider = StateProvider<int>((ref) {
  print('CounterProvider が初期化されました');
  return 0;
});
```

### 2. 初期化 (Initialization)

Provider が最初に読み取られたとき、つまり `.watch()` や `.read()` メソッドが呼び出されたときに初期化されます。この時点で Provider の値が計算され、メモリに保持されます。

### 3. 依存関係の追跡 (Dependency Tracking)

Riverpod の強力な機能の一つは、Provider 間の依存関係を自動的に追跡することです。あるプロバイダーが別のプロバイダーを監視（watch）すると、依存関係が確立されます。

```dart
final doubledCounterProvider = Provider<int>((ref) {
  // counterProvider への依存関係を確立
  final count = ref.watch(counterProvider);
  return count * 2;
});
```

### 4. 更新 (Update)

Provider の値が変更されると、その Provider を監視しているすべての依存関係に通知が送られ、再評価されます。

### 5. 破棄 (Disposal)

Provider が不要になると、そのリソースを解放するために破棄されます。これには主に二つのケースがあります：

1. `.autoDispose` モディファイアを使用していて、Provider に監視者がいなくなった場合
2. `ref.onDispose()` コールバックが実行される場合（例：ProviderScope が破棄されるとき）

```dart
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 は、監視者がいなくなると自動的に破棄されます。

```dart
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 も破棄されます。

```dart
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 の状態変化を監視し、その変化に反応することができます。

```dart
ref.listen<int>(counterProvider, (previous, next) {
  if (previous != null && next > previous) {
    print('カウンターが増加しました: $previous -> $next');
  }
});
```

このリスナーは、`ref` オブジェクトが属する Provider または Widget が破棄されるまで有効です。

## まとめ

Riverpod の Provider は以下のライフサイクルを持ちます：

1. **作成** - Provider が定義されるとき
2. **初期化** - Provider が最初に使用されるとき
3. **依存関係の追跡** - Provider が他の Provider を監視するとき
4. **更新** - Provider の値が変更されるとき
5. **破棄** - Provider が不要になり、リソースが解放されるとき

Riverpod はこのライフサイクルを通じて、効率的なメモリ管理と状態管理を実現します。`autoDispose` モディファイアやオーバーライドの仕組みを活用することで、さらに柔軟な状態管理が可能になります。

適切にライフサイクルを理解し、活用することで、パフォーマンスが高く、メンテナンスしやすいFlutterアプリケーションを構築することができます。