[官文翻译]Flutter状态管理库Riverpod - 所有的Provider - ChangeNotifierProvider

1,007 阅读2分钟

Riverpod的官方文档有多国语言,但是没有汉语,所以个人简单翻译了一版。

官网文档:Riverpod

GitHub:GitHub - rrousselGit/river_pod

Pub:riverpod | Dart Package (flutter-io.cn)

译时版本:riverpod 1.0.3


ChangeNotifierProvider

ChangeNotifierProvider (仅用于 flutter_riverpod/hooks_riverpod)是用于Flutter 自身监听和暴露 ChangeNotifier 的 Provider 。

Riverpod 不建议使用 ChangeNotifierProvider ,只为以下几种情况保留:

  • 使用 package:providerChangeNotifierProvider 进行简单过滤时。
  • 支持可变状态,即使不可变状态更好用。

信息
更倾向于使用 StateNotifierProvider 代替 只在绝对确定想要使用可变状态时考虑使用 ChangeNotifierProvider

有时使用可变状态代替不可变状态会更高效。 副作用是,难于维护,并可能破坏各种功能。

例如,如果状态可变,使用 provider.select 改善组件的重新构建时可能不起作用,因为 select 会认为值没有改变。

就此而论,使用不可变数据结构有时会更快。 因此,为使用场景创建性能指标很重要,要确保通过使用 ChangeNotifierProvider 确实能提高性能。

作为用法示例,我们使用 ChangeNotifierProvider 实现了一个 todo 列表。 这样做允许我们暴露 如 addTodo 的方法使 UI 在用户交互时改变 todo 列表:

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

  String id;
  String description;
  bool completed;
}

class TodosNotifier extends ChangeNotifier {
  final todos = <Todo>[];

  // 让 UI 添加 todo
  void addTodo(Todo todo) {
    todos.add(todo);
    notifyListeners();
  }

  // 可移除 todo
  void removeTodo(String todoId) {
    todos.remove(todos.firstWhere((element) => element.id == todoId));
    notifyListeners();
  }

  // 标记 todo 为完成
  void toggle(String todoId) {
    for (final todo in todos) {
      if (todo.id == todoId) {
        todo.completed = !todo.completed;
        notifyListeners();
      }
    }
  }
}

// 最终,使用 StateNotifierProvider 允许 UI 和 TodosNotifier 类交互。
final todosProvider = ChangeNotifierProvider<TodosNotifier>((ref) {
  return TodosNotifier();
});

现在我们已经定义了 ChangeNotifierProvider,我们可以在 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).todos;

    // 在可滚动的列表视图中渲染 todo
    return ListView(
      children: [
        for (final todo in todos)
          CheckboxListTile(
            value: todo.completed,
            // 点击 todo 时,改变完成状态
            onChanged: (value) =>
                ref.read(todosProvider.notifier).toggle(todo.id),
            title: Text(todo.description),
          ),
      ],
    );
  }
}