尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Riverpod Todo 示例深度解析:用 Notifier 与派生 Provider 构建可响应式待办应用

发布时间:2026/9/28 2:34:13

资讯中心
01
ARTICLE

Riverpod Todo 示例深度解析:用 Notifier 与派生 Provider 构建可响应式待办应用

Riverpod Todo 示例深度解析:用 Notifier 与派生 Provider 构建可响应式待办应用
前端移动开发【免费下载链接】riverpodA reactive caching and>项目地址https://gitcode.com/gh_mirrors/ri/riverpod点击查看免费下载导读本文以 Riverpod 仓库中的 examples/todos 示例应用为核心完整讲解如何用 Riverpod 构建一个功能完备的待办清单Todo List应用从NotifierProvider管理复杂列表状态、StateProvider管理简单过滤器状态到用纯Provider缓存并自动派生“未完成数量”与“过滤后列表”等计算结果再到用ProviderScope.overrides做作用域注入。读完本文你将掌握 Riverpod 的“状态分层 自动缓存 精准重建”这套进阶实战组合拳。该示例是 Riverpod 官方仓库中“比入门更进阶”的展示正如 examples/todos/README.md 所述它重点演示“更进阶的状态操作”slightly more advanced state manipulation其核心技术点在旧版本中被称为Computed如今已与Provider合并见 packages/riverpod/CHANGELOG.md 的记录因此本文所有分析均基于当前仓库代码展开。一、示例概览一个“麻雀虽小五脏俱全”的进阶 demoexamples/todos是一个使用hooks_riverpodflutter_hooks构建的待办应用功能包括添加待办文本框回车提交勾选/取消勾选完成状态点击条目进入行内编辑失焦或回车时保存左滑Dismissible删除待办按 All / Active / Completed 三种视图过滤列表实时显示剩余未完成数量。其依赖声明见 examples/todos/pubspec.yaml应用同时依赖riverpod核心状态管理、hooks_riverpodFlutter 绑定 Hooks 扩展、flutter_hooks与uuid生成待办 id并通过resolution: workspace与仓库根目录的 Dart workspace 关联。从代码结构看该示例的架构刻意做了“状态与 UI 分离”的分层设计这是理解本文所有内容的总纲层文件职责数据模型examples/todos/lib/todo.dart不可变的Todo模型 TodoList业务逻辑状态声明examples/todos/lib/main.dart各类 provider 的声明与派生计算UI 层examples/todos/lib/main.dartHome、Toolbar、TodoItem等组件测试examples/todos/test/widget_test.dart覆盖增、删、改、过滤、计数等交互的 widget 测试二、状态分层四种 Provider 各司其职main.dart中按“状态的复杂度”选择了四种不同的 provider这一分层思路值得反复品味。1.NotifierProvider承载复杂业务逻辑的列表状态待办列表是一个需要 add/toggle/edit/remove 四种操作的复杂对象因此使用NotifierProviderTodoList, ListTodo承载main.dartfinal todoListProvider NotifierProviderTodoList, ListTodo( TodoList.new, name: todoListProvider, );对应的TodoList定义在 examples/todos/lib/todo.dartclass TodoList extends NotifierListTodo { override ListTodo build() [ const Todo(id: todo-0, description: Buy cookies), const Todo(id: todo-1, description: Star Riverpod), const Todo(id: todo-2, description: Have a walk), ]; void add(String description) { state [...state, Todo(id: _uuid.v4(), description: description)]; } void toggle(String id) { state [ for (final todo in state) if (todo.id id) Todo(id: todo.id, completed: !todo.completed, description: todo.description) else todo, ]; } void edit({required String id, required String description}) { state [ for (final todo in state) if (todo.id id) Todo(id: todo.id, completed: todo.completed, description: description) else todo, ]; } void remove(Todo target) { state state.where((todo) todo.id ! target.id).toList(); } }几个值得注意的实现细节build()提供初始状态应用启动即有三条预置待办且为 widget 测试提供了稳定的初始数据测试中直接断言Buy cookies、Star Riverpod、Have a walk三条记录见 widget_test.dart。不可变性所有修改都通过“基于旧状态构造新列表”完成展开语法[...state]或for集合推导从不原地修改。这与 Riverpod 基于状态变更而非引用相同性触发重建的模型完全契合。通过ref.read(todoListProvider.notifier)调用方法UI 层不直接操作状态而是调用Notifier暴露的业务方法例如输入框提交时ref.read(todoListProvider.notifier).add(value)main.dart。2.StateProvider承载无逻辑的简单枚举过滤器只是一个枚举值没有附加业务逻辑因此用StateProvider足够main.dartenum TodoListFilter { all, active, completed } final todoListFilter StateProvider( (_) TodoListFilter.all, name: todoListFilter, );UI 中通过ref.read(todoListFilter.notifier).state TodoListFilter.active直接赋值main.dart。注释明确给出了选型理由“这里没有操纵值的复杂逻辑它只是枚举”no fancy logic behind manipulating the value since its just enum。3.Provider缓存派生计算本示例的技术核心README 中点名的Computed在现在的版本中即对应Provider。它用于“从其他 provider 派生并缓存计算结果”。main.dart中有两个典型用例未完成数量main.dartfinal uncompletedTodosCount Providerint((ref) { return ref.watch(todoListProvider).where((todo) !todo.completed).length; }, name: uncompletedTodosCount);过滤后的列表main.dartfinal filteredTodos ProviderListTodo((ref) { final filter ref.watch(todoListFilter); final todos ref.watch(todoListProvider); switch (filter) { case TodoListFilter.completed: return todos.where((todo) todo.completed).toList(); case TodoListFilter.active: return todos.where((todo) !todo.completed).toList(); case TodoListFilter.all: return todos; } }, name: filteredTodos);这两个 provider 完美体现了Provider即旧Computed的三条核心语义代码注释逐条做了说明只算一次即使多个 widget 同时watch未完成数量计算也只在依赖变化时执行一次其余时候命中缓存只重算必要的编辑待办文字不会改变“未完成数量”因此编辑时该 provider 不会重建相关 widget 也不会无谓重建按依赖精准重建filteredTodos同时watch过滤器与列表两者任一变化才会重新计算。ref.watch的底层实现印证了这一切——在 packages/riverpod/lib/src/core/ref.dart 中watch等价于“订阅依赖 依赖变化时失效自身并重建”的组合sub _element.listenStateT( listenable, (prev, value) _invalidateSelf(asReload: true, manual: false), ... );这也解释了为何Provider天然具备“取消订阅”能力当派生 provider 重建后发现不再依赖某个旧 provider 时会自动释放监听CHANGELOG 中记录了Computed的这一修复见 packages/riverpod/CHANGELOG.md。4.Provider的另一种用法作用域注入_currentTodoTodoItem展示的单个Todo也通过 provider 传递这是本文第三个重点技巧main.dart/// A provider which exposes the [Todo] displayed by a [TodoItem]. final _currentTodo ProviderTodo( dependencies: const [], name: _currentTodo, (ref) throw UnimplementedError(), );它的默认实现直接抛UnimplementedError真正的值由Home构建列表时通过ProviderScope的overrides注入main.dartDismissible( key: ValueKey(todos[i].id), onDismissed: (_) { ref.read(todoListProvider.notifier).remove(todos[i]); }, child: ProviderScope( overrides: [_currentTodo.overrideWithValue(todos[i])], child: const TodoItem(), ), )这样做的好处代码注释交代得很清楚TodoItem可以保持const构造避免每次添加/删除/编辑时整条列表全部重建每个TodoItem通过ref.watch(_currentTodo)只拿到自己的那条Todomain.dart因此只有“受影响的那一项”会重建其余条目完全不动。这正是“精准重建”在 UI 层的落地数据通过 provider 传递而不是通过构造函数层层下传从而把 widget 树的变更范围压缩到最小。三、UI 层如何与状态联动1. 根组件ProviderScope包裹一切main()中runApp(const ProviderScope(child: MyApp()))main.dartProviderScope是所有 provider 的根容器widget 测试中同样以ProviderScope(child: MyApp())作为测试宿主widget_test.dart保证测试环境与真实运行一致。2.HookConsumerWidgetHooks 与 Riverpod 的结合页面组件统一继承HookConsumerWidget如Home、Toolbar、TodoItem从而在build中同时获得两个能力ref参数调用ref.watch/ref.read与状态交互Hooks APIuseTextEditingController()、useFocusNode()管理输入框与焦点main.dart。例如Home通过ref.watch(filteredTodos)拿到过滤后的列表并渲染main.dartToolbar通过ref.watch(uncompletedTodosCount)显示剩余数量main.dart。值得注意的是useIsFocused是一个自定义 hookmain.dart用useState记录焦点状态useEffect中注册FocusNode监听器并在销毁时移除。它驱动了“聚焦时显示输入框、失焦时提交编辑”的交互逻辑——这是 hooks 模式“封装可复用逻辑”的典型示范。3. 编辑提交的时机性能导向的设计TodoItem的编辑提交刻意放在“失焦”时机main.dartonFocusChange: (focused) { if (focused) { textEditingController.text todo.description; } else { // Commit changes only when the textfield is unfocused, for performance ref .read(todoListProvider.notifier) .edit(id: todo.id, description: textEditingController.text); } },注释直言“出于性能考虑仅在输入框失焦时提交”。这意味着每次按键并不会触发edit、重建列表只有完成编辑失焦或按回车时才一次性写入状态。widget 测试也严格验证了这两种提交路径“失焦提交”widget_test.dart与“回车提交”widget_test.dart。四、测试验证每个行为都有据可查examples/todos/test/widget_test.dart 为上述所有交互提供了可运行的验证包括渲染默认待办三条预置待办及其未勾选状态含 macOS 专属 golden 测试initial_state.png勾选切换勾选后“3 items left”变为“2 items left”直接验证了uncompletedTodosCount的派生更新widget_test.dart失焦/回车编辑输入新描述后旧文本消失、新文本出现左滑删除Dismissible手势后条目消失过滤视图勾选第一条后点 Active第一条消失、其余保留widget_test.dart添加待办回车提交后输入框清空、列表出现新条目、计数变为 4含 golden 测试new_todo.png。这些测试既是对示例功能的回归保护也是读者理解 provider 派生行为的最佳“说明书”每一条断言背后都是一次状态变更—派生重算—UI 重建的完整链路。五、运行与延伸在仓库根目录执行flutter pub get或通过 Dart workspace 自动解析见 pubspec.yaml 的resolution: workspace后即可运行该示例flutter test可执行全部 widget 测试。如果想基于本示例继续探索 Riverpod可以顺藤摸瓜阅读Provider/Notifier的完整 API 语义ref.watch的失效机制见 packages/riverpod/lib/src/core/ref.dartComputed合并进Provider的历史脉络见 packages/riverpod/CHANGELOG.md依赖失效与“仅重算必要部分”的底层测试见 packages/riverpod/test/old/framework/ref_watch_test.dart其中直接验证了派生 provider 在多个依赖分别变化时的重建次数与监听释放行为。小结examples/todos用一份不到 300 行的main.dart把 Riverpod 的进阶用法浓缩成了可运行、可测试的范例NotifierProvider承载业务逻辑、StateProvider承载简单状态、Provider缓存派生计算、ProviderScope.overrides实现按条目注入。它回答了一个核心问题——如何让“状态变化后只有真正受影响的部分重算与重建”而这正是 Riverpod 相对传统setState方案的核心价值所在。赞分享前端移动开发【免费下载链接】riverpodA reactive caching and>项目地址https://gitcode.com/gh_mirrors/ri/riverpod点击查看免费下载相关推荐Jotai Todos 示例全解析用原子状态与派生 Atom 构建可过滤待办列表Jotai Todos 示例全解析用原子状态与派生 Atom 构建可过滤待办列表 导读 本文围绕 Jotai 仓库中的 todos 示例 https://li前端状态管理LinGoose架构设计解析深入理解模块化AI框架的内部机制LinGoose架构设计解析深入理解模块化AI框架的内部机制 在当今AI应用开发浪潮中如何快速构建稳定、可扩展的大语言模型应用成为开发者面临的重要挑战。 L大模型RAG人工智能后端从计数器到待办清单用Iced构建响应式Rust GUI应用从计数器到待办清单用Iced构建响应式Rust GUI应用 Iced是一个受Elm启发的跨平台Rust GUI库采用响应式编程模型让开发者能够以简洁、类型前端跨平台UI组件桌面应用上一篇Claude SEO 的 Banana 图像生成成本追踪指南从定价模型到 cost_tracker.py 实战下一篇Minified.js实用函数库日期格式化、数字处理与集合操作的终极指南 创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。