一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情。
Riverpod的官方文档有多国语言,但是没有汉语,所以个人简单翻译了一版。
官网文档:Riverpod
GitHub:GitHub - rrousselGit/river_pod
Pub:riverpod | Dart Package (flutter-io.cn)
译时版本:riverpod 1.0.3
StateNotifierProvider
StateNotifierProvider
是用来监听和暴露 StateNotifier (在 state_notifier 包中,Riverpod 又将其重新导出) 的 provider 。
和 StateNotifier 一起使用的 StateNotifierProvider
是 Riverpod 建议的管理状态的解决方案,这可能会改变用户交互的响应。
这通常用于:
- 暴露 不可变的 状态,该状态会在自定义事件响应后跟着改变。
- 集中逻辑用于在单个位置更改一些状态(又叫做 业务逻辑),不断改善可维护性。
作为一个用法示例,我们可以使用 StateNotifierProvider
实现 TODO 列表。这样做允许我们暴露如 addTodo
的方法使 UI 在用户交互时更新 TODO 列表:
// StateNotifier 的状态应该是不可改变的。
// 我们也可以使用如 Freezed 的包帮助我们实现。
@immutable
class Todo {
const Todo({required this.id, required this.description, required this.completed});
// 类中的所有属性都就该是 `final` 的。
final String id;
final String description;
final bool completed;
// 由于 Todo 是不可改变的,我们实现一个允许使用稍微不同内容克隆 TODO 的方法。
Todo copyWith({String? id, String? description, bool? completed}) {
return Todo(
id: id ?? this.id,
description: description ?? this.description,
completed: completed ?? this.completed,
);
}
}
// StateNotifier 类会被传递给 StateNotifierProvider 。
// 这个类不应该对外暴露它的 “state” 属性,这意味着没有 public 的 getter/属性 !
// 用于该类的 public 的方法 会允许 UI 更改状态。
class TodosNotifier extends StateNotifier<List<Todo>> {
// 我们初始化 TODO 列表为一个空列表
TodosNotifier(): super([]);
// 允许 UI 添加 TODO 。
void addTodo(Todo todo) {
// 因为我们的状态是不可改变的,所以不允许使用 `state.add(todo)` 。
// 我们应该创建一个包含以前项目和新 TODO 项目的新列表。
// 这里使用 Dart 的 spread 操作符比较有帮助!
state = [...state, todo];
// 不需要调用 "notifyListener" 或类似的方法。
// 调用 "state =" 会在必要时自动重新构建 UI 。
}
// 允许移除 TODO
void removeTodo(String todoId) {
// 同样,我们的状态是不可改变的。所以我们会创建一个新列表代替现有的列表。
state = [
for (final todo in state)
if (todo.id != todoId) todo,
];
}
// 标记 TODO 为完成
void toggle(String todoId) {
state = [
for (final todo in state)
// 我们只将匹配的 TODO 标记为完成
if (todo.id == todoId)
// 再说一次,因为我们的状态是不可改变的,我们需要复制该 TODO。
// 我们使用前面实现的 `copyWith` 方法帮忙做该处理。
todo.copyWith(completed: !todo.completed)
else
// 其它的 TODO 没有改变。
todo,
];
}
}
// 最后,我们使用 StateNotifierProvider 允许 UI 响应 TodosNotifier 类。
final todosProvider = StateNotifierProvider<TodosNotifier, List<Todo>>((ref) {
return TodosNotifier();
});
现在我们已经定义了一个 StateNotifierProvider
,可以使用它在 UI 中和 TODO 列表交互:
class TodoListView extends ConsumerWidget {
const TodoListView({Key? key}): super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
// TODO 列表改变时重新构建组件
List<Todo> todos = ref.watch(todosProvider);
// 在可滚动的 ListView 中渲染 TODO
return ListView(
children: [
for (final todo in todos)
CheckboxListTile(
value: todo.completed,
// 点击 TODO 时,改变它的 status
onChanged: (value) => ref.read(todosProvider.notifier).toggle(todo.id),
title: Text(todo.description),
),
],
);
}
}