👿

【Riverpod学習】Providerの種類〜ChangeNotifierProviderについて〜

2024/10/14に公開

以下の公式を読みながらChangeNotifierProviderについて勉強したのでその備忘録です

https://riverpod.dev/docs/providers/change_notifier_provider

ChangeNotifierProviderは、FlutterのChangeNotifierクラスを使って状態を管理するためのRiverpodのプロバイダです。これは、packageのproviderからの移行を容易にするために提供されていますが、Riverpodではあまり推奨されてなく、一般的に不変の状態管理を推奨しているため、NotifierProviderを使う方が推奨されます。

1. ChangeNotifierProviderとは?

ChangeNotifierProviderは、FlutterのChangeNotifierクラスを使用して可変の状態を管理するプロバイダです。ChangeNotifierは、リストやオブジェクトなどの可変の状態を簡単に管理でき、notifyListeners() を呼び出すことで、状態が変更されたことをリスナー(UI)に通知します。

2. 推奨されない理由

Riverpodでは、不変(immutable)な状態管理が推奨されています。これにより、状態の変更が明確になり、バグを防ぎやすく、アプリ全体のパフォーマンスが向上する可能性があります。ChangeNotifierは状態が可変であり、適切に管理しないと状態の変更が追跡しにくくなることがあります。

しかし、すでにChangeNotifierベースのコードがある場合や、どうしても可変の状態管理が必要な場合に、ChangeNotifierProviderを使用することができます。

3. ChangeNotifierProviderの使い方

例: Todoリストの管理
次に、ChangeNotifierProviderを使ってTodoリストを管理する方法を説明します。

1. Todoクラスの定義

Todoクラスは、タスクを表すシンプルなオブジェクトです。

class Todo {
  Todo({
    required this.id,
    required this.description,
    required this.completed,
  });

  String id;           // タスクのID
  String description;  // タスクの内容
  bool completed;      // タスクが完了しているかどうか
}

2. TodosNotifierクラスの定義

TodosNotifierクラスは、ChangeNotifierを継承して、Todoリストの追加、削除、完了状態の切り替えなどのロジックを提供します。状態が変更された際にnotifyListeners()を呼び出し、UIが自動的に再描画されます。

class TodosNotifier extends ChangeNotifier {
  final todos = <Todo>[];  // Todoリストを保持

  // Todoを追加するメソッド
  void addTodo(Todo todo) {
    todos.add(todo);
    notifyListeners();  // 状態変更を通知してUIを再描画
  }

  // Todoを削除するメソッド
  void removeTodo(String todoId) {
    todos.removeWhere((element) => element.id == todoId);
    notifyListeners();
  }

  // Todoの完了状態を切り替えるメソッド
  void toggle(String todoId) {
    final todo = todos.firstWhere((todo) => todo.id == todoId);
    todo.completed = !todo.completed;  // 完了状態を反転
    notifyListeners();
  }
}

3. ChangeNotifierProviderの定義

次に、ChangeNotifierProviderを使ってTodosNotifierクラスをプロバイダとして提供します。

final todosProvider = ChangeNotifierProvider<TodosNotifier>((ref) {
  return TodosNotifier();
});

todosProviderは、TodosNotifierのインスタンスを生成してUIで利用できるようにするプロバイダです。

4. UIでの使用方法

次に、todosProviderを使ってTodoリストを表示し、ユーザーがタスクを操作できるUIを実装します。

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

  
  Widget build(BuildContext context, WidgetRef ref) {
    // todosProviderの状態を監視し、Todoリストを取得
    List<Todo> todos = ref.watch(todosProvider).todos;

    // Todoリストを表示する
    return ListView(
      children: [
        for (final todo in todos)
          CheckboxListTile(
            value: todo.completed,
            // チェックボックスをタップして完了状態を切り替える
            onChanged: (value) => ref.read(todosProvider.notifier).toggle(todo.id),
            title: Text(todo.description),
          ),
      ],
    );
  }
}

ポイント

ref.watch(todosProvider): todosProviderの状態(TodosNotifierのtodosリスト)を監視し、状態が変更されるたびにUIが再描画されます。
ref.read(todosProvider.notifier).toggle(todo.id): ユーザーがチェックボックスを操作すると、TodosNotifierのtoggleメソッドが呼び出され、Todoの完了状態が変更されます。

4. ChangeNotifierProviderの注意点

  • 不変の状態管理の方が推奨される: Riverpodでは、不変の状態管理を推奨しています。ChangeNotifierProviderは可変状態を扱うため、管理が複雑になりがちです。
  • NotifierProviderの方が推奨される: 可変状態が必要な場合でも、RiverpodではNotifierProviderを使う方が推奨されています。StateNotifierを使うことで、状態の管理がより明確になり、長期的なメンテナンス性が向上します。

5. まとめ

ChangeNotifierProviderは、FlutterのChangeNotifierを使って可変の状態を管理します。状態が変更されたときにnotifyListeners()を使ってUIを再描画する仕組みです。
可変の状態を扱うため、状態管理が複雑になりがちで、Riverpodでは不変の状態管理を推奨しています。そのため、NotifierProviderの使用が推奨されています。
ChangeNotifierProviderは既存のChangeNotifierベースのコードを使用している場合に役立ちますが、状態の管理が必要な場合には、NotifierProviderへの移行を検討すべきです。

Discussion