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 是这个类的构造函数,会传给 provider,让它可以创建这个 notifier。
关键规则:给 state 赋一个新值,绝不要修改旧值。只有在 state 被设为另一个对象时,Riverpod 才会通知各个 widget。state.add(name) 会直接修改原列表,所以 state 仍然是同一个列表,不会触发通知。[...state, name] 会创建一个新列表:三个点会把所有旧名字展开到新列表中,然后把新名字放在末尾。
widget 可以通过 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 在线编译器