Flutter 状态管理选型 2026:Riverpod vs Bloc vs setState
Flutter 的状态管理选项比 React 还多——很离谱。每个都有死忠粉。这是一份在生产里真的用每种方案发版过之后的诚实对比。
先分类状态
跟任何 UI 框架一样:
- 临时状态 — 动画控制器、文本编辑、展开折叠。住在
State<>里。 - 应用状态 — 登录、主题、购物车、多页向导。需要工具。
- 服务端状态 —
dio/http+ 缓存层管。
常见错误:用同一种工具对待临时和应用状态。给 bool isExpanded 单独建 Riverpod provider 是过度设计;用 setState 管"当前登录用户"又是耦合不够。我自己踩过最深的坑就是后者——一个购物车页面全靠顶层 setState 顶着,每次加一件商品整页 rebuild,下拉位置丢、动画闪,调了一晚上才认清这是工具用错了不是 bug。
setState 够用的场景
如果你的页面大多自包含,setState + InheritedWidget 真能撑起一个完整 app。只在以下情况伸手要外部库:
- 多个页面要共享同一份可变数据。
- 有非平凡的派生状态。
- 想要不依赖 widget 测试业务逻辑。
Riverpod 3 — 现代默认
Riverpod 是 provider 该长成的样子:不依赖 BuildContext、编译期类型安全、代码生成消除 90% 样板。
class CartNotifier extends _$CartNotifier {
List<CartItem> build() => [];
void add(CartItem item) => state = [...state, item];
void clear() => state = [];
}
// 在 widget 里:
final cart = ref.watch(cartNotifierProvider);
@riverpod + riverpod_generator 帮你写 Provider 模板代码。Riverpod 3 把 autoDispose 设为默认,异步 provider 组合自然。
选 Riverpod 适合:
- 新应用
- 想让业务逻辑能脱离 widget 测试
- 团队接受代码生成工具链
不适合:团队抵触代码生成工具的话。
Bloc / Cubit — 当显式事件流是目的本身
Bloc 强迫你按"事件 → 状态"思考。这既是优势也是负担。
class CartCubit extends Cubit<List<CartItem>> {
CartCubit() : super([]);
void add(CartItem item) => emit([...state, item]);
}
Cubit 是 "lite 版":扔掉事件类层级。用它替代 Riverpod 也行,只是仪式感更重。
完整 Bloc 闪光时刻:
- 受监管行业(金融、医疗),需要可审计的事件日志。
- 大团队,靠强约定 + lint 降低 review 成本。
- 跨 Bloc 通信(
BlocListener)契合你的心智模型。
不要在小应用上为"未来扩展"用 Bloc。那个未来很少到来。
Signals — 新晋选手
signals_flutter 把 SolidJS 风格的细粒度反应式带到 Flutter。适合:
- 你从 JS 来,怀念 Solid/Vue 的手感。
- 想要外科手术级的重渲染又不想写 selector。
生态相对年轻,能不能用看团队对早期工具的耐受度。
四种方案横向对比
把这几年发版的体感压成一张表,选型时扫一眼:
| 方案 | 适用规模 | 样板量 | 主要踩坑点 |
|---|---|---|---|
| setState | 单页 / 自包含 widget | 几乎为零 | 滥用到跨页共享时整页 rebuild,状态散落难追踪 |
| Provider | 小到中型 | 中等 | 依赖 BuildContext,深层嵌套容易 ProviderNotFound,类型安全弱 |
| Riverpod 3 | 中到大型新项目 | 低(代码生成扛走大半) | 要接受 codegen 工具链,build_runner watch 偶尔卡;老项目从 Provider 迁移有学习成本 |
| Bloc / Cubit | 大团队 / 受监管行业 | 高(事件类层级) | 小应用上仪式感拖慢迭代,事件-状态映射写多了显啰嗦 |
我实际怎么选
- 业余项目:Riverpod 3 + riverpod_generator +
dio+cached_network_image。三行 provider 搞定。 - 20 人团队大型应用:Bloc,因为团队达成共识,约定降低 review 成本。仪式感是 feature 不是 bug。
- 单页原型:setState。真的。
反模式
- 全局 ChangeNotifier 挂在 MaterialApp 上重建整棵树。 用
Consumer+buildWhen做 selector。 - Provider + Bloc + Riverpod 同一项目混用。 选一个,迁移成本远低于维护三个的成本。
- setState 管异步状态。 用 FutureProvider/AsyncNotifierProvider 拿到 loading/error/data 三元组——第一次请求失败你就感谢自己。
一句话总结
- 2026 新项目 → Riverpod 3 + 代码生成。
- 大团队强约定 → Bloc。
- 小自包含 UI → setState。
- 服务端数据 → dio/http + 缓存,不是状态库。
对错答案随团队规模变化,多过随框架版本变化。
我们是一支开发者小团队,负责构建和维护 ToolsForge —— 150+ 个完全在浏览器本地运行、隐私优先的工具。这些文章来自我们日常的工程实践,而不是纸上谈兵。
了解我们相关阅读
React 状态管理 2026:Context、Zustand、Jotai 还是 Redux Toolkit?
2026 年最实用的 React 状态管理选型对比 — Context、Zustand、Jotai、Redux Toolkit 与 React Compiler 时代的新选择。附上选用规则、代码示例与迁移建议。
一张照片能做多少事:AI 改图工具实战指南
换发型、换发色、换装、转卡通、风格迁移、室内设计、老照片修复——用一张照片走完这些 AI 改图流程,以及怎么让结果更稳、更像本人。
把脸保住:AI 改图提示词怎么写才不"换人"
换发型、换装、换风格时,AI 老把脸也改了?这篇讲清楚改图提示词的写法:保人约束、双通道(gpt-image-2 / Nano Banana)各自的脾气,以及一套能直接抄的模板。