Riverpod: Notifier
CoddyのFlutterジャーニー「状態管理」セクションの一部。レッスン 19/27。
StateProviderは数値ひとつなら問題ありません。追加や削除のメソッドを持つリストのように、状態にルールが伴う場合は、状態とそれを変更するメソッドを保持するクラスであるNotifierを書きます。
class GuestList extends Notifier<List<String>> {
@override
List<String> build() => [];
void add(String name) {
state = [...state, name];
}
}
final guestsProvider =
NotifierProvider<GuestList, List<String>>(GuestList.new);buildは最初の状態である空のリストを返します。クラス内では、stateが現在の値です。GuestList.newはクラスのコンストラクターで、プロバイダーがNotifierを作成できるように渡します。
重要なルールは、stateには新しい値を設定し、古い値は決して変更しないことです。Riverpodは、stateに別のオブジェクトが設定されたときにだけウィジェットに通知します。state.add(name)はリストをその場で変更するため、stateは同じリストのままで、誰にも通知されません。[...state, name]は新しいリストを作ります。3つのドットは古い名前をすべて新しいリストに展開し、その後、新しい名前を末尾に追加します。
ウィジェットはNotifierを介してメソッドを呼び出します。ref.read(guestsProvider.notifier).add('Ada')。このページはConsumerStatefulWidget、つまりステートフルな種類です。テキストコントローラーも保持するためです。そのStateはフィールドとしてrefを持ちます。
チャレンジ
難しいゲストリストにはguestsProvider内の名前が表示されますが、Addを押しても入力欄がクリアされるだけです。
GuestListにメソッドadd(String name)を追加し、名前を末尾に追加した新しいリストをstateに設定してください。- 入力欄をクリアする前に、
Addから入力欄のテキストを渡してそのメソッドを呼び出すようにしてください。
自分で試してみよう
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class GuestList extends Notifier<List<String>> {
@override
List<String> build() => [];
}
final guestsProvider = NotifierProvider<GuestList, List<String>>(GuestList.new);
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: PartyPage(),
);
}
}
class PartyPage extends ConsumerStatefulWidget {
const PartyPage({super.key});
@override
ConsumerState<PartyPage> createState() => _PartyPageState();
}
class _PartyPageState extends ConsumerState<PartyPage> {
final controller = TextEditingController();
@override
Widget build(BuildContext context) {
final guests = ref.watch(guestsProvider);
return Scaffold(
appBar: AppBar(title: Text('Guests: ${guests.length}')),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: TextField(
controller: controller,
decoration: const InputDecoration(labelText: 'Guest name'),
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () {
controller.clear();
},
child: const Text('Add'),
),
],
),
),
Expanded(
child: ListView(
children: [
for (final name in guests) ListTile(leading: const Icon(Icons.person), title: Text(name)),
],
),
),
],
),
);
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
状態管理のすべてのレッスン
4Riverpod
Riverpod: ProviderScopeConsumerWidgetとref.watchRiverpod: StateProviderRiverpod: Notifier他のProviderを監視するProvider状態の置き場所を選ぶ自分で練習してみよう: Flutterオンラインコンパイラ