4、状态管理选型:Provider vs Bloc vs GetX vs Riverpod,选型对比与实战
状态管理,说白了就是 Flutter 项目里的「数据大管家」。
我见过不少团队,一开始随便选了个方案,结果项目做到一半发现扩展不动了,重构成本高得吓人。所以这一章,咱们就把市面上最主流的四个方案掰开揉碎,看看它们到底适合什么场景。
4.1 为什么需要状态管理?
先问个问题:你写 Flutter 时,有没有遇到过「A 页面改了数据,B 页面死活不更新」的情况?
这就是典型的「状态共享」问题。Flutter 本身是声明式 UI,数据变了,UI 要跟着变。但如果没有一个统一的管理机制,你只能靠回调一层层传,代码很快就变成一团乱麻。
我个人习惯是:小项目用 setState 就够了,但一旦超过 3 个页面需要共享数据,就必须上状态管理框架。
4.2 Provider:官方推荐,入门首选
Provider 是 Google 官方推荐的方案,也是我最早接触的状态管理库。它的核心思想就一句话:把数据放到顶层,谁用谁拿。
核心特点:
- 基于 InheritedWidget 封装,学习成本低
- 配合 ChangeNotifier 使用,写法直观
- 社区生态成熟,文档齐全
来个最简单的例子:
class CounterProvider extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 通知 UI 更新
}
}
// 使用
final counter = context.watch<CounterProvider>();
Text('${counter.count}');
嗯,这里要注意:context.watch 会让整个 widget 重建。如果你只想读数据不重建,用 context.read。
我的经验:Provider 适合中小型项目,特别是团队里新手比较多的时候。我在一个电商 App 里用过 Provider,页面层级不深,开发效率确实高。但一旦涉及到跨页面复杂状态同步,Provider 的嵌套问题就开始让人头疼了。
4.3 Bloc:企业级项目的「正规军」
Bloc 全称是 Business Logic Component,说白了就是把业务逻辑和 UI 彻底分离。
我第一次用 Bloc 时觉得它太啰嗦了——一个简单的计数器要写事件、状态、Bloc 三个文件。但后来在大型项目中,这种「啰嗦」反而成了优势。
核心特点:
- 强制使用事件驱动,逻辑清晰可追溯
- 支持响应式编程,测试友好
- 适合多人协作的大型项目
看个计数器例子:
// 事件
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
// 状态
class CounterState {
final int count;
CounterState(this.count);
}
// Bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(0)) {
on<IncrementEvent>((event, emit) {
emit(CounterState(state.count + 1));
});
}
}
// UI 使用
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text('${state.count}');
},
);
避坑指南:我曾经在一个项目中过度使用 Bloc,每个小组件都搞一个 Bloc,结果文件数量爆炸。后来我总结了一个原则:只有跨页面共享的复杂逻辑才用 Bloc,页面内部的简单状态用 setState 就够了。
4.4 GetX:轻量级「瑞士军刀」
GetX 是个「全能型选手」,它不光管状态,还管路由、依赖注入、国际化……说白了,它想让你少写很多样板代码。
核心特点:
- 零依赖,包体积小
- 语法简洁,开发效率极高
- 内置路由和依赖管理
GetX 的计数器写法,你感受一下:
class CounterController extends GetxController {
var count = 0.obs; // 可观察变量
void increment() => count++;
}
// UI 使用
final controller = Get.put(CounterController());
Obx(() => Text('${controller.count}'));
是不是很简洁?但简洁背后也有代价。
我的看法:GetX 适合快速原型开发,或者个人项目。但我在团队项目里用过一次后,发现它的「魔法」太多了——Get.put、Get.find 这些全局方法让依赖关系变得隐晦,新人接手时很难理清数据流向。说白了,GetX 的灵活性是把双刃剑。
4.5 Riverpod:Provider 的「进化版」
Riverpod 是同一个作者对 Provider 的反思和重构。它解决了 Provider 的几个痛点:编译时安全、不依赖 BuildContext、支持异步状态。
核心特点:
- 不依赖 Widget 树,可以在任何地方访问
- 编译时类型安全,减少运行时错误
- 原生支持异步和缓存
Riverpod 的写法:
final counterProvider = StateProvider<int>((ref) => 0);
// UI 使用
final count = ref.watch(counterProvider);
Text('$count');
// 更新
ref.read(counterProvider.notifier).state++;
我的推荐:如果你正在新项目选型,我个人建议优先考虑 Riverpod。它吸收了 Provider 的简洁,又弥补了它的不足。我在一个中型项目中从 Provider 迁移到 Riverpod,迁移成本不高,但代码的可维护性提升了一个档次。
4.6 选型对比表
| 维度 | Provider | Bloc | GetX | Riverpod |
|---|---|---|---|---|
| 学习曲线 | 低 | 高 | 低 | 中 |
| 代码量 | 中 | 多 | 少 | 中 |
| 测试友好度 | 中 | 高 | 低 | 高 |
| 团队协作 | 中 | 高 | 低 | 高 |
| 适合项目规模 | 中小型 | 大型 | 小型/原型 | 中大型 |
| 社区生态 | 成熟 | 成熟 | 活跃 | 快速增长 |
4.7 实战建议:我到底该选哪个?
说实话,没有「最好」的状态管理,只有「最合适」的。我根据项目类型给几个建议:
- 个人练手或小工具:GetX,开发快,代码少
- 3-5 人团队的中型项目:Riverpod,平衡了简洁和规范
- 10 人以上的大型企业项目:Bloc,强制规范,方便代码审查
- 团队新手多,需要快速上手:Provider,文档多,坑少
最后提醒一句:不管你选哪个方案,一定要在项目初期就定下来。我见过太多项目做到一半换状态管理方案,那感觉就像给高速行驶的汽车换轮胎——不是不能换,但代价太大了。
下一章,咱们会基于本章的选型结论,搭建一个完整的项目骨架。到时候我会用 Riverpod 做演示,因为它在企业级项目里确实表现不错。当然,如果你选了 Bloc 或 Provider,核心架构思路是相通的。