StateNotifierProvider - 状態の公開と高度な状態操作
状態を格納し、監視できるStateNorifierを使用して、状態を公開・操作できるProvider。
StateProviderとの大きな違いは、単純な状態の保持だけでなく、状態を操作するメソッドをStateNotifierを継承したクラスに定義できること。
StateProviderを使った場合に、Widgetが状態操作のロジックで溢れてしまう場合もStateNotifierの使用を検討しましょう。
StateNotifierを継承したクラスを作成
Providerの作成の前に、必要なクラスを作成しましょう。
TODOのリストを State(状態)に持ち、TODOを操作するクラスの例です。
todoListNotifier.dart
class TodoListNotifier extends StateNotifier<List<Todo>> {
// `super([])` で、空のTODOリストを初期値として入れている。
const TodoListNotifier(): super([]);
/// 新しいTODOを追加するメソッド
void add(Todo todo) {
state = [...state, todo];
}
/// IDを指定して、TODOを削除するメソッド
void remove(String todoId) {
state = [
for (final todo in state)
if (todo.id != todoId) todo,
];
}
/// IDを指定して、TODOの完了状態を切り替えるメソッド
void toggle(String todoId) {
state = [
for (final todo in state)
if (todo.id == todoId)
todo.copyWith(completed: !todo.completed),
];
}
}
Providerの宣言
作成した TodoListNotifier
のProviderを宣言します。
専用のファイルを作成して書いても良いですし、 class TodoListNotifier
の上に書いても良いと思います。
todoListNotifier.dart
// Providerの定数をグローバルに宣言
// StateNotifierProviderの後に続けて、Notifierクラスの型と、格納する状態の型を明示する
final todoListProvider = StateNotifierProvider<TodoListNotifier, List<Todo>>(
(ref) => TodoListNotifier(),
);
Widgetで使用する
todoListPage.dart
Widget build(BuildContext context, ScopedReader watch) {
// StateNotifierProviderを読み取る。watchを使用しているので、
// state(状態)であるTODOリストが更新されると、buildメソッドが再実行されて画面が更新される
final todoList = watch(todoListProvider);
// TodoListNotifier を使用する場合は `.notifier` を付けてProviderを読み取る
final notifier = watch(todoListProvider.notifier);
return Scaffold(
body: ListView(
children: [
for (final todo in todoList)
ListTile(
// TODOのタイトルをTextで表示
title: Text(todo.title),
// タップでTODOの完了状態を切り替える
onTap: () => notifier.toggle(todo.id),
)
],
),
);
}
もう一つの書き方
buildメソッド内ではなく、ボタンを押した時などに Notifier
を読み取って使用する記法として以下のようにも書けます。
onTap: () => context.read(todoListProvider.notifier).toggle(todo.id),
詳しくは、「context.readでwatchせずにProviderを利用する」の章で解説します。
参考リンク
StateNotifierProvider | Riverpodドキュメントページ