当前位置: 首页 > news >正文

Flutter MVVM实战:用Provider和Riverpod分别重构一个Todo App,聊聊我的选择

Flutter MVVM实战:Provider与Riverpod的Todo App重构对比

当Flutter开发者面临状态管理方案选择时,Provider和Riverpod总是出现在候选名单的前列。我在最近一个Todo应用的重构过程中,分别用这两种方案实现了完整的MVVM架构。本文将分享从项目初始化到功能完成的完整对比体验,包括代码组织差异、响应式机制实现、测试便利性等实战细节。

1. 项目背景与架构设计

Todo应用作为经典案例,包含了状态管理的典型需求:列表展示、增删改查操作和状态联动。MVVM架构将业务逻辑与UI解耦的特性,正好匹配这类交互密集型应用的需求。

基础架构组件对比

组件Provider实现方案Riverpod实现方案
Model层纯Dart类纯Dart类(与Provider方案一致)
ViewModel层ChangeNotifier的子类StateNotifier的子类
状态通知机制notifyListeners()调用state属性更新
依赖注入MultiProvider层级结构ProviderScope全局容器

在项目初始化阶段,Riverpod的setup明显更简洁。不需要在Widget树中层层包裹Provider,只需在根节点使用单个ProviderScope:

void main() { runApp(ProviderScope(child: MyApp())); }

而Provider方案则需要维护复杂的Provider树:

void main() { runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => TodoViewModel()), ], child: MyApp(), ), ); }

2. ViewModel实现细节对比

ViewModel作为MVVM架构的核心枢纽,两种方案的实现差异直接影响代码结构和可维护性。

2.1 Provider方案实现

基于ChangeNotifier的ViewModel需要手动管理状态通知:

class TodoViewModel extends ChangeNotifier { final List<TodoItem> _todos = []; List<TodoItem> get todos => _todos; void addTodo(String title) { _todos.add(TodoItem(title: title)); notifyListeners(); // 必须显式调用 } // 其他操作方法... }

典型痛点

  • 容易忘记调用notifyListeners()
  • 状态变更逻辑分散在各个方法中
  • 需要手动维护不可变状态

2.2 Riverpod方案实现

StateNotifier提供了更结构化的状态管理:

class TodoViewModel extends StateNotifier<List<TodoItem>> { TodoViewModel() : super([]); void addTodo(String title) { state = [...state, TodoItem(title: title)]; // 自动触发更新 } // 其他操作方法... }

优势对比

  • 状态变更通过state赋值自动通知
  • 强制使用不可变状态模式
  • 所有状态变更集中处理

实际开发中发现,Riverpod的状态不可变性要求虽然增加了初期学习成本,但显著减少了由意外状态修改引发的bug。

3. 视图层集成对比

两种方案在UI层的集成方式体现了不同的设计哲学。

3.1 组件状态订阅

Provider使用Consumer或context.watch:

Consumer<TodoViewModel>( builder: (context, viewModel, child) { return ListView.builder( itemCount: viewModel.todos.length, itemBuilder: (context, index) => TodoItem( item: viewModel.todos[index] ), ); }, )

Riverpod则通过watch函数实现:

final viewModel = ref.watch(todoViewModelProvider); return ListView.builder( itemCount: viewModel.length, itemBuilder: (context, index) => TodoItem( item: viewModel[index] ), );

3.2 操作触发方式

Provider需要通过context读取实例:

onPressed: () { context.read<TodoViewModel>().addTodo('New item'); }

Riverpod则通过provider.notifier:

onPressed: () { ref.read(todoViewModelProvider.notifier).addTodo('New item'); }

性能考量

  • Riverpod的编译时安全机制可以避免常见的provider查找错误
  • Provider的context依赖在大型项目可能导致widget重建范围过大

4. 测试与可维护性对比

在编写单元测试时,两种方案的差异更加明显。

4.1 Provider测试方案

test('should add todo item', () { final viewModel = TodoViewModel(); viewModel.addTodo('Test item'); expect(viewModel.todos.length, 1); });

需要额外处理ChangeNotifier的监听机制。

4.2 Riverpod测试方案

test('should add todo item', () { final container = ProviderContainer(); addTearDown(container.dispose); container.read(todoViewModelProvider.notifier).addTodo('Test item'); expect(container.read(todoViewModelProvider).length, 1); });

测试优势

  • 完整的依赖注入环境
  • 可以模拟整个provider层次
  • 状态变更更易断言

在实现撤销/重做功能时,Riverpod的不可变状态模式展现出更大优势。只需维护状态历史列表即可:

class TodoViewModel extends StateNotifier<List<TodoItem>> { final List<List<TodoItem>> _history = []; void addTodo(String title) { _history.add(state); state = [...state, TodoItem(title: title)]; } void undo() { if (_history.isNotEmpty) { state = _history.removeLast(); } } }

5. 开发体验与决策建议

经过完整项目实践,两种方案各有适用场景:

选择Provider当

  • 项目已使用Provider生态
  • 需要快速上手简单项目
  • 团队熟悉Flutter传统模式

选择Riverpod当

  • 项目需要长期维护扩展
  • 重视编译时安全
  • 需要更灵活的状态组合
  • 考虑未来迁移到Flutter 3.0+

实际开发中,Riverpod的以下特性显著提升了效率:

  • 自动dispose管理
  • 家族参数支持
  • 更细粒度的rebuild控制
  • 与freezed的结合使用
// 配合freezed实现极致类型安全 @freezed class TodoState with _$TodoState { factory TodoState({ required List<TodoItem> items, required FilterType filter, }) = _TodoState; } final todoViewModelProvider = StateNotifierProvider.autoDispose< TodoViewModel, TodoState >((ref) => TodoViewModel());

对于新启动的中大型项目,Riverpod的综合优势已经非常明显。但在小规模原型开发中,Provider的简洁性仍然具有吸引力。

http://www.rkmt.cn/news/1526640.html

相关文章:

  • 2026年 隔离变压器厂家/电气隔离变压器/安全隔离变压器/抗干扰隔离变压器/电源隔离净化变压器十大品牌精选推荐 - 品牌发掘
  • 广州电商税务风险咨询机构排行:合规服务实力对比 - 互联网科技品牌测评
  • 联发科设备深度操作指南:MTKClient逆向工程与底层控制技术解析
  • Transformer 注意力机制变体与长序列建模优化:从 O(n²) 到线性注意力的工程演进
  • 【深度解析】OpenRouter Fusion API 技术拆解:多模型融合架构的能力边界与工程实践
  • YOLOv8生菜生长周期识别检测系统(项目源码+YOLO数据集+模型权重+UI界面+python+深度学习+环境配置)
  • 戴森球计划工厂蓝图库:5000+优化设计助力星际工业化建设
  • 买到了冒牌货的内存条----山寨内存条-----------是正规的
  • 怎样用Layerdivider智能图层分离工具:3步实现专业级图像分层
  • G4Splat:用几何骨架为生成式先验“立规矩”——ICLR 2026 稀疏视角三维重建新范式
  • 2026年多级泵厂家推荐榜:辽阳立式/卧式/不锈钢/高压/节能/深井/供水/高层增压及工业高压多级泵品牌实力解析 - 品牌发掘
  • 开发记录18_相似人脸不等于同一个人_身份聚类与向量索引
  • 全平台开源AI助手,让AI直接生成可交互的界面
  • 专门把视频里焊死的硬字幕去掉,不会糊成马赛克,处理完还是原片分辨率
  • 终极指南:3分钟快速掌握B站视频解析的完整解决方案
  • [Android] 动漫天堂最新版-免费看动漫-极速无广
  • 崩坏3扫码登录工具:9大渠道服一键登录的终极解决方案
  • Redis 从入门到精通:性能调优与多语言客户端对比
  • [Android] 软眠眠-治愈系白噪音睡眠监测助眠工具
  • Redis 从入门到精通:Python + Redis 构建高并发秒杀系统
  • 会MySQL就会 Elasticsearch?这个国产框架做到了
  • 2026年离心泵源头厂家推荐榜单:辽阳单级/双吸/卧式/立式/不锈钢/防爆/耐酸碱/高温/化工泵全方位品质解析 - 品牌发掘
  • 终极指南:使用openFPGALoader快速编程300+ FPGA开发板
  • 右键秒算哈希:Windows文件校验神器HashCheck完全指南
  • 茂名市黄金回收三家门店实地探店综合测评 - 靖昱黄金回收
  • [Android] 题有有-中小学拍照找题组卷学习工具
  • 3步解锁中兴光猫工厂模式:zteOnu工具完整使用指南
  • 2026荆州全屋家装公司名录:核心维度客观对比 - 互联网科技品牌测评
  • 计算机Java毕设实战-基于 SpringBoot 的水果库存与购物管理系统的设计与实现 现代化生鲜水果电商信息化管理系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 2026荆州全屋家装标杆名录 本地靠谱品牌客观盘点 - 互联网科技品牌测评