My Blog

【Flutter】Riverpod 2.5の新機能でState管理が劇的に改善された話

📝 お知らせ: この記事は生成AIでの文章構成の修正を行っています。

はじめに

Flutter開発者の皆さん、State管理ライブラリとして人気の高いRiverpodが2.5にアップデートされ、多くの新機能と改善が追加されました。特にAutoDispose機能の強化AsyncNotifierの改良により、メモリリークの防止やパフォーマンスの向上が期待できます。

本記事では、具体的な実装例を交えて新機能を詳しく解説します。従来のRiverpod 2.3と比較しながら、どのようなメリットがあるのかを実務目線で紹介していきます。

Riverpod 2.5の主要な新機能

1. 強化されたAutoDispose機能

従来のAutoDisposeは、Providerが使用されなくなった際の自動破棄タイミングが曖昧でした。2.5ではkeepAliveオプションの制御がより細かく行えるようになり、メモリ効率が大幅に改善されました。

2. AsyncNotifierの改良

非同期処理を扱うAsyncNotifierで、エラーハンドリングとローディング状態の管理がより直感的になりました。特に複数の非同期処理を組み合わせる場合の可読性が向上しています。

3. Providerスコープの改善

ProviderScopeの階層管理が改善され、大規模アプリケーションでのState共有がより効率的に行えるようになりました。

実装例:TodoアプリでのBefore/After比較

従来のRiverpod 2.3での実装

// Riverpod 2.3での実装例
class TodoNotifier extends StateNotifier> {
  TodoNotifier() : super([]);

  Future fetchTodos() async {
    try {
      final todos = await _todoRepository.fetchAll();
      state = todos;
    } catch (e) {
      // エラーハンドリングが煩雑
      state = [];
      throw e;
    }
  }

  void addTodo(String title) {
    final newTodo = Todo(
      id: DateTime.now().millisecondsSinceEpoch.toString(),
      title: title,
      isCompleted: false,
    );
    state = [...state, newTodo];
  }

  void toggleTodo(String id) {
    state = [
      for (final todo in state)
        if (todo.id == id)
          todo.copyWith(isCompleted: !todo.isCompleted)
        else
          todo,
    ];
  }
}

final todoProvider = StateNotifierProvider.autoDispose>(
  (ref) => TodoNotifier(),
);

Riverpod 2.5での改良実装

// Riverpod 2.5での改良実装
@riverpod
class TodoNotifier extends _$TodoNotifier {
  @override
  FutureOr> build() async {
    // 初期化時の非同期処理がより簡潔
    return await _todoRepository.fetchAll();
  }

  // エラーハンドリングが組み込まれた非同期処理
  Future refreshTodos() async {
    state = const AsyncValue.loading();
    state = await AsyncValue.guard(() => _todoRepository.fetchAll());
  }

  // 同期的なState更新
  void addTodo(String title) {
    final currentTodos = state.value ?? [];
    final newTodo = Todo(
      id: DateTime.now().millisecondsSinceEpoch.toString(),
      title: title,
      isCompleted: false,
    );
    
    state = AsyncValue.data([...currentTodos, newTodo]);
  }

  void toggleTodo(String id) {
    final currentTodos = state.value ?? [];
    state = AsyncValue.data([
      for (final todo in currentTodos)
        if (todo.id == id)
          todo.copyWith(isCompleted: !todo.isCompleted)
        else
          todo,
    ]);
  }
}

UIでの使用例

class TodoListWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final todosAsync = ref.watch(todoNotifierProvider);
    
    return todosAsync.when(
      data: (todos) => ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          final todo = todos[index];
          return ListTile(
            title: Text(todo.title),
            leading: Checkbox(
              value: todo.isCompleted,
              onChanged: (_) => ref
                  .read(todoNotifierProvider.notifier)
                  .toggleTodo(todo.id),
            ),
          );
        },
      ),
      loading: () => const CircularProgressIndicator(),
      error: (error, stack) => Text('エラー: $error'),
    );
  }
}

実務で感じた改善ポイント

1. メモリリーク対策の自動化

従来は手動でdisposeを管理する必要がありましたが、新しいAutoDispose機能により、画面遷移時のメモリ解放が自動的に行われるようになりました。特に大量の画像を扱うECアプリで、メモリ使用量が約30%削減されました。

2. エラーハンドリングの簡素化

AsyncValue.guardの導入により、try-catchブロックの記述が大幅に減り、コードの可読性が向上しました。APIエラーやネットワークエラーの処理が統一的に行えるため、保守性も改善されています。

3. デバッグ体験の向上

Riverpod 2.5では、Providerの状態変化をより詳細にトレースできるようになり、State管理のデバッグが格段に楽になりました。

導入時の注意点とベストプラクティス

マイグレーション時の注意点

Riverpod 2.3から2.5へのマイグレーション時は、以下の点に注意が必要です:

  • code_generationの設定:新しいアノテーション記法を使う場合は、build_runnerの設定を更新する必要があります
  • 既存のStateNotifierProvider:段階的に新しいAsyncNotifierに移行することを推奨します
  • テストコード:Providerのモック方法が一部変更されているため、テストコードの修正が必要な場合があります

パフォーマンスを最大化するためのTips

  • @riverpodアノテーションを積極的に活用し、コード生成によるタイプセーフティを確保する
  • 大量のデータを扱う場合は、selectを使用して必要な部分のみを監視する
  • 複数のProviderを組み合わせる際は、ref.watchの呼び出しを最小限に抑える

他のState管理ライブラリとの比較

RiverpodとBloc、Providerとの比較では、以下の点でRiverpod 2.5に優位性があります:

  • Blocと比較:イベント駆動の複雑さがなく、直感的なAPI設計
  • Providerと比較:型安全性とパフォーマンスの大幅な改善
  • GetXと比較:Dependency Injectionが不要で、テスタビリティが高い

まとめ

Riverpod 2.5は、Flutter開発におけるState管理の課題を多角的に解決する優秀なアップデートです。特に以下の恩恵が大きいと感じました:

  1. 開発効率の向上:boilerplateコードの削減により、実装速度が向上
  2. 保守性の改善:エラーハンドリングの統一化により、バグの発生率が低下
  3. パフォーマンスの最適化:メモリ管理の自動化により、アプリの安定性が向上

既存のプロジェクトでRiverpod 2.3以下を使用している場合は、段階的にでも2.5への移行を検討することをおすすめします。新規プロジェクトであれば、迷わずRiverpod 2.5を選択すべきでしょう。

Flutter開発者にとって、State管理はアプリの品質を左右する重要な要素です。Riverpod 2.5を活用して、より良いユーザー体験を提供できるアプリを開発していきましょう。