当前位置: 首页 > 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.jsqmd.com/news/1014823/

相关文章:

  • YOLO小目标检测救星:实测CARAFE对比双线性插值/反卷积,mAP提升多少?
  • 2026深圳电商财税合规公司排行:3家标杆服务商维度对比 - 互联网科技品牌测评
  • 嵌入式测试学习第 36 天:串口日志分析、通过日志定位简单问题
  • 联发科设备深度操作指南:MTKClient逆向工程与底层控制技术解析
  • 5分钟快速上手缠论分析:通达信免费插件完全指南
  • 广州电商税务风险咨询机构排行:合规服务实力对比 - 互联网科技品牌测评
  • 【深度解析】OpenRouter Fusion API 技术拆解:多模型融合架构的能力边界与工程实践
  • BiliDownload终极指南:如何高效获取B站无水印视频的完整教程
  • Pandas数据清洗六大实战Hack:性能优化与工程化实践
  • Transformer 注意力机制变体与长序列建模优化:从 O(n²) 到线性注意力的工程演进
  • 2026年 隔离变压器厂家/电气隔离变压器/安全隔离变压器/抗干扰隔离变压器/电源隔离净化变压器十大品牌精选推荐 - 品牌发掘
  • YOLOv8生菜生长周期识别检测系统(项目源码+YOLO数据集+模型权重+UI界面+python+深度学习+环境配置)
  • 【技术干货】Kimi K2.7 Code 深度拆解:MCP工具调用超越Claude,开源编程模型新标杆
  • 从星载SAR到微型无人机SAR:分辨率公式背后的工程权衡与选型指南
  • Claude Code 实战:AI 结对编程如何真正提效:从踩坑到可复用方案
  • 2026年液位计厂家推荐排行榜:吉林磁翻板/玻璃管/浮球/雷达/超声波/防爆/就地/水箱/储罐/工业/污水池液位计品牌深度测评 - 品牌发掘
  • AI CAD图纸一秒检索怎么实现
  • 巴西市政公司开源模型杀进全球第一、Google把300万颗TPU交给英特尔、A股重回4000点
  • eSDHC控制器:从硬件信号到软件驱动的嵌入式SD卡存储系统解析
  • 深耕广东房企资质服务赛道,广州融景企业管理集团打造房地产开发二级资质代办标杆品牌 - 广东科技观察
  • 革命性Python百度搜索API:免费无限制的智能搜索引擎集成方案
  • 如何彻底解决Windows和Office激活问题:KMS_VL_ALL_AIO智能激活方案完全指南
  • 戴森球计划工厂蓝图库:5000+优化设计助力星际工业化建设
  • 弥赛亚叙事:学术赵高,数学鬼才,牛顿封神的认知病毒
  • 怎样用Layerdivider智能图层分离工具:3步实现专业级图像分层
  • 把二维照片变成能旋转查看的3D模型,做设计搞开发玩创意的都值得试试
  • 2026潍坊劳动律师怎么选?5个实战判断标准不踩雷 - 本地品牌推荐
  • G4Splat:用几何骨架为生成式先验“立规矩”——ICLR 2026 稀疏视角三维重建新范式
  • 买到了冒牌货的内存条----山寨内存条-----------是正规的
  • 2026中国薪酬咨询机构专业评测:从体系搭建到改革落地的实战指南 - 互联网科技品牌测评