前言
昨天 get 的删库跑路之后发,社区和公司部门内部基本可以说是原地炸了。(早上作者说是他的github账户被风控,但是get本身的问题已经很多了...)
问得最多的,不是"这个包现在还能不能装",而是另一类更扎心的问题:
"以后 Flutter 项目状态管理到底该怎么选?"
"Get 还能不能继续用?"
"Provider、Riverpod、Bloc 这些,到底谁更靠谱?"
"你如果现在开一个新项目,你会选谁?"
我仔细思考了,发现很多讨论都有两个毛病:
- 只聊 API,不聊项目演进后的维护成本。
- 只聊自己喜欢什么,不聊团队、业务、复杂度、学习成本这些现实问题。
所以这篇文章,我想和大家聊聊我的看法。
我想做的事情很简单:
借着这次 get 的风波,把 Flutter 生态里主流的状态管理方案,重新摆到桌面上,按真实项目的标准,认认真真聊一遍。
不是聊"哪个最优雅",而是聊:
- 它到底解决什么问题
- 它的边界在哪
- 它为什么有人爱,也为什么有人骂
- 如果现在重新开一个项目,我会怎么选
为了避免这篇文章变成"空对空",我还顺手把同一个业务场景做成了一个开源 Demo,把 Provider / Riverpod / Cubit / Bloc Event 全都落地了一遍。
开源地址在这里: state_manages
后面我文中提到的一些对比,不只是嘴上说说,基本都能在这个仓库里对上代码。

先说结论
我先把结论放前面,免得大家看半天最后发现和自己预想差不多。
如果你现在问我:
"2026 年这个时间点,Flutter 新项目状态管理怎么选?"
我的答案是:
- 小项目、单人项目、快速起步:Provider
- 中大型新项目、我个人最愿意推荐的平衡方案:Riverpod
- 多人协作、复杂业务、强调规范和状态流可追踪:Cubit / Bloc
- 老项目已经深度绑定 GetX:先稳住,不要一激动就全量重构
- 新项目再从 0 开始选 GetX:我个人会明显更谨慎
注意,我这里不是说 GetX 技术上突然一夜归零了。
而是说,这次事情把一个以前很多人不愿意正视的问题,硬生生摊开了:
状态管理从来不是"代码写起来爽不爽"这么简单,它还关乎维护、协作、生态稳定性、升级路线、团队兜底能力。
说得再直白一点:
以前大家觉得"能跑就行", 这次很多人才开始意识到:
"哦,原来依赖生态稳定,也是技术选型的一部分。"
篇章一:先把问题掰直,状态管理到底在管什么
很多人聊状态管理,一上来就对比 API:
setStatenotifyListenersref.watchemitObx
但这其实是表象。
状态管理真正要解决的,不是"你用哪个函数刷新页面",而是下面这几件事:
- 状态放在哪里
- 状态变化后,谁来通知 UI
- 异步请求、空状态、错误状态怎么建模
- 页面越来越复杂之后,代码会不会开始失控
- 团队里第二个人、第三个人接手时,还看不看得懂
你会发现,一个状态管理方案,真正的价值,不在于它能不能写出页面。
因为大家都能写。
真正拉开差距的,是当业务变成这样时:
- 页面有列表
- 列表要刷新
- 请求会失败
- 有搜索
- 有筛选
- 有排序
- 还有弹 Toast、弹 Dialog、跳详情页这种一次性副作用
这时候你再看,方案之间的差异就出来了。
也就是说:
状态管理的核心,不是"能不能更新 UI",而是"当 UI 和业务越来越复杂时,这套结构还能不能顶住"。
篇章二:Provider,Flutter 状态管理里的"老实人"
如果让我给 Provider 起个外号,我会叫它:
"老实人方案。"
它最大的特点就是:
- 不花
- 不绕
- 不装神秘
- 你基本一眼就能知道状态在哪、怎么改、谁在监听
这也是为什么,很多 Flutter 新人第一个真正上手的状态管理,都是它。
Provider 到底在干嘛
最常见的写法其实很直白:
dart
class UserViewModel extends ChangeNotifier {
bool loading = false;
List<String> users = [];
Future<void> loadUsers() async {
loading = true;
notifyListeners();
await Future.delayed(const Duration(seconds: 1));
users = ['Ava', 'Noah', 'Mia'];
loading = false;
notifyListeners();
}
}
// 页面里用:
ChangeNotifierProvider(
create: (_) => UserViewModel()..loadUsers(),
child: Consumer<UserViewModel>(
builder: (_, vm, __) {
if (vm.loading) {
return const CircularProgressIndicator();
}
return ListView(
children: vm.users.map(Text.new).toList(),
);
},
),
)
这套东西的优点几乎不用解释:
- 好懂
- 上手快
- 学习门槛低
- 代码量不大
- 对小页面非常够用
Provider 为什么好用
因为它特别符合人脑最朴素的思路:
- 我有一个对象
- 对象里放状态
- 改完状态
- 通知页面刷新
这套逻辑没有什么抽象负担。
对于很多简单页面来说,这种方案不仅够用,而且其实是最划算的。
你非要拿一个很轻的用户列表页,上来就写一堆事件、状态类、派生结构,很多时候反而是技术过剩。
Provider 的问题到底在哪
问题不在它不能用,而在它太容易一路"长歪"。
最开始你只放两个字段:
- loading
- users
后面慢慢加:
- errorMessage
- query
- selectedFilter
- sortType
- showVipOnly
- currentTab
- hasMore
- isRefreshing
再往后还会加一堆方法:
- loadUsers
- refreshUsers
- retry
- updateQuery
- toggleVip
- changeSort
- openDetail
- showErrorToast
写着写着,一个 ChangeNotifier 就变成了一个"巨型大管家"。
它不是不能维护,但它特别考验开发者的自觉。
Provider 最大的问题,不是功能弱,而是结构约束弱。
你写得好,它很好用。 你写得随便,它也很容易烂。
所以 Provider 适合谁
我会这样建议:
适合:
- Flutter 初学者
- 小型项目
- 页面级逻辑不复杂的业务
- 想先把状态管理基本感觉建立起来的人
不太适合:
- 中大型复杂项目当唯一主状态管理方案
- 多人长期协作、对结构一致性要求很高的团队
- 派生状态很多、异步链路复杂的模块
篇章三:Riverpod,我最愿意推荐给新项目的方案
如果说 Provider 是"老实人",那 Riverpod 在我眼里更像:
"脑子清楚、结构现代、能打硬仗的中生代主力。"
我为什么这么说?
因为 Riverpod 真正厉害的地方,不是"语法多高级",而是:
它很擅长把依赖关系和状态关系拆清楚。
这点在项目越做越大时,价值会越来越明显。
Riverpod 和 Provider 最本质的区别
很多人会把 Riverpod 理解成"升级版 Provider"。
这么说不算全错,但也太粗暴了。
Provider 更像是:
- 往 Widget Tree 里塞对象
- 下层从树里读对象
- 对象变了,通知相关 Widget 刷新
Riverpod 更像是:
- 先把状态和依赖拆成一个个 provider 节点
- provider 和 provider 之间可以互相组合
- 页面只是去消费这些节点
你把它想象成一张依赖图,会更容易理解。
Riverpod 为什么在异步场景特别舒服
我觉得 Riverpod 最讨喜的一点,是它对异步状态的表达非常自然。
比如一个最常见的异步列表:
dart
final usersProvider =
AsyncNotifierProvider<UsersNotifier, List<String>>(UsersNotifier.new);
class UsersNotifier extends AsyncNotifier<List<String>> {
@override
Future<List<String>> build() async {
await Future.delayed(const Duration(seconds: 1));
return ['Ava', 'Noah', 'Mia'];
}
Future<void> refreshUsers() async {
state = const AsyncLoading();
state = await AsyncValue.guard(() async {
await Future.delayed(const Duration(seconds: 1));
return ['Ava', 'Noah', 'Mia'];
});
}
}
// 页面里:
final asyncUsers = ref.watch(usersProvider);
return asyncUsers.when(
loading: () => const CircularProgressIndicator(),
error: (e, _) => Text('出错了:$e'),
data: (users) => ListView(
children: users.map(Text.new).toList(),
),
);
你会发现这里有个很明显的优势:
异步状态本身就是框架的一等公民。
不是你自己去维护:
- isLoading
- errorMessage
- hasData
而是用 AsyncValue 直接把这些状态表达出来。
这个对实际开发体验影响很大。
Riverpod 真正强的,不是异步,而是"组合能力"
如果你只拿一个简单异步列表示例去看 Riverpod,其实还没看到它最强的地方。
它真正强的是这种场景:
- 原始用户列表一个 provider
- 搜索关键词一个 provider
- VIP 开关一个 provider
- 排序方式一个 provider
- 最终可见列表再是一个派生 provider
- 用户详情页再用 family
也就是说:
Riverpod 不是鼓励你写一个"大而全的状态类",而是鼓励你把不同职责拆成多个 provider,再组合起来。
这会带来两个很现实的好处:
- 结构更清楚
- 重建范围更好控制
Riverpod 有什么代价
它当然也不是白给的。
代价主要有三个:
1. 学习曲线比 Provider 高
你第一次看 Riverpod,脑子里经常会冒出几个问题:
- 为什么一个页面拆这么多 provider
- 为什么这里 watch,那边 read
- 为什么这里要 listen
- 为什么 provider 还要依赖 provider
这很正常,因为 Riverpod 不是在教你"存一个对象",而是在教你"组织一组状态节点"。
2. 写不好会显得很碎
Riverpod 的优点是可拆分,但坏处也正是可拆分。
如果一个团队没有统一规范,很容易出现:
- provider 命名混乱
- 分层过细
- 逻辑散落到各处
最后导致不是"结构清晰",而是"文件一大堆,人都找不到"。
3. 对抽象能力有要求
Riverpod 更适合那种愿意先想清楚状态边界,再写代码的人。
如果一个人习惯先堆功能,再慢慢补结构,那 Riverpod 反而不一定让他更轻松。
所以 Riverpod 适合谁
我的建议是:
非常适合:
- 中大型新项目
- 需要长期维护的项目
- 依赖关系复杂、派生状态较多的模块
- 想把局部注入、测试隔离做得更清晰的团队
如果你问我现在新项目更倾向推荐谁,
我个人会优先推荐 Riverpod。
不是因为它最火,也不是因为它"最优雅",而是因为它在:
- 开发体验
- 异步表达
- 可组合性
- 可维护性
- 模块化能力
这几个维度上,整体太均衡了。
篇章四:Bloc / Cubit,这套东西不是"重",而是"规矩大"
很多人一提到 Bloc,第一反应就是:
"太重了。"
这句话不能说错,但我觉得它只说了一半。
更准确一点的说法应该是:
Bloc 不是单纯地重,它是规矩大。
它会逼着你把一些以前可以"糊着写"的东西,全部摊开来写清楚。
比如:
- 页面到底触发了什么动作
- 动作进来后,状态怎么变
- 哪些地方是副作用
- 哪些地方只是纯渲染
这套思路,在简单页面里确实显得重。
但一旦业务复杂起来,它的价值就会越来越大。
先说 Cubit,它比你想象中实用
我其实很想先替 Cubit 正个名。
因为很多人把 Bloc 体系一股脑都理解成:
- 一堆 event
- 一堆 state
- 一堆 boilerplate
但 Cubit 不是这样的。
Cubit 更像是:
"有明确状态对象的、工程化一点的 ViewModel。"
比如:
dart
class UsersCubit extends Cubit<UsersState> {
UsersCubit() : super(const UsersState());
Future<void> loadUsers() async {
emit(state.copyWith(loading: true));
await Future.delayed(const Duration(seconds: 1));
emit(
state.copyWith(
loading: false,
users: ['Ava', 'Noah', 'Mia'],
),
);
}
}
你看,它其实很好懂:
- 有状态类
- 有方法
- 改状态时 emit
- 页面用 BlocBuilder 监听
对很多简单到中等复杂度页面来说,Cubit 是非常实用的。
它比 Provider 更有"状态层"的味道, 又比完整 Bloc Event 版轻很多。
那为什么还需要 Bloc Event 版
因为业务一复杂,方法驱动就开始不够清楚了。
比如一个页面同时有:
- 首次加载
- 刷新
- 重试
- 搜索变更
- 排序切换
- 筛选切换
- Toast 提示
- 并发请求控制
这时候你再全靠方法名去表达,就会慢慢开始乱。
而 Event 版会逼你把事情说清楚:
- UsersRequested
- UsersRefreshed
- UsersRetried
- SearchChanged
- FilterChanged
这不是为了多写几个类,而是为了让状态变化路径可追踪。
尤其在多人协作里,这点特别重要。
Bloc 这套方案最值钱的地方
我觉得有三点。
1. 事件语义明确
一眼就能看出:
"页面现在到底发生了什么业务动作。"
2. 副作用边界清楚
用 BlocBuilder 渲染 UI, 用 BlocListener 处理 Toast、Dialog、路由跳转。
这比很多项目里"状态逻辑和副作用搅成一锅粥"的写法,要清爽太多。
3. 复杂交互下更稳
比如并发控制。
Bloc 生态里你可以明确去处理:
- 重复点击刷新怎么办
- 搜索输入连发怎么办
- 模式切换时要保留最后一次还是顺序执行
这些东西,在复杂项目里不是"锦上添花",而是迟早会遇到的坑。
Bloc 的代价是什么
说实话,代价也很明显:
- 样板代码更多
- 初学者上手成本更高
- 简单页面里容易显得重炮打蚊子
所以我不会无脑推荐所有人都上 Bloc Event 版。
但如果你在的团队是这种风格:
- 人多
- 业务重
- 状态复杂
- 强调规范
- 维护周期长
那 Bloc 的价值,真的会越来越明显。
所以 Bloc / Cubit 适合谁
我的建议是:
- 简单页面:Cubit 很香
- 复杂业务模块:Bloc Event 版很稳
- 团队已经全套 flutter_bloc:不要轻易引第二套主状态管理
- 如果只是个人小项目,别一上来就给自己加戏
篇章五:GetX,到底该怎么重新看
这部分我不想写成"清算大会"。
因为说实话,GetX 当年能火,不是没原因的。
它确实帮很多 Flutter 开发者解决过实际问题。
尤其是早几年,Flutter 生态还没现在这么成熟时,GetX 的那种"开箱快、上手爽、什么都给你带一点"的感觉,对很多人真的很有吸引力。
GetX 当年为什么能打
原因其实很现实:
- 学起来快
- 写起来省事
- 状态管理、路由、依赖注入几乎一把梭
- 对很多从前端框架过来的人很有亲和力
你写页面时会感觉:
"卧槽,这也太快了吧。"
这就是 GetX 当年最强的传播力来源。
但它的问题也一直存在
只不过以前很多人选择忽略。
我自己总结,主要有这几类:
1. 职责容易混在一起
GetX 很容易让人一路写成这种结构:
- 状态也在 controller
- 路由也在 controller
- 依赖注入也在 controller
- 页面副作用也在 controller
- 工具方法也在 controller
最后 controller 既像 ViewModel,又像 Service,又像 Router。
短期开发很爽,长期看边界其实很容易糊。
2. "魔法感"很强
很多写法前期很丝滑,但越往后越容易出现一种感觉:
项目能跑,但你说不清它到底靠什么机制在跑。
这对个人项目问题不大, 但对团队维护来说,是个隐患。
3. 风险被低估了
以前大家更多讨论的是:
- 性能
- 写法
- 学习曲线
这次事情之后,大家终于被迫意识到另一层风险:
生态稳定性和治理能力,也是技术选型的一部分。
所以我现在对 GetX 的态度是:
- 我不会否认它历史上的价值
- 我也不会说所有 GetX 项目都得马上重构
- 但如果你现在让我从 0 开一个新项目,我会明显更谨慎
篇章六:如果今天重新开一个项目,我会怎么选
这一段我尽量说人话,不打太极。
场景一:单人项目、验证想法、快速上线
我会优先考虑:
- Provider
- Cubit
原因很简单:
- 成本低
- 起步快
- 心智负担小
别把事情搞太复杂。
场景二:中小团队新项目,业务会持续增长
我会优先考虑:
- Riverpod
原因是它太平衡了。
它既没有 Bloc Event 那么重, 又比 Provider 更容易把结构撑住。
如果团队里成员整体水平还不错,Riverpod 是一个非常舒服的主状态管理方案。
场景三:复杂业务、多人协作、强调规范
我会优先考虑:
- Cubit + Bloc Event
简单模块用 Cubit, 复杂模块上 Bloc Event。
这种搭配很实用。
场景四:老项目已经深度用了 GetX
我不会建议你们一夜重构。
我的建议是:
- 先锁版本
- 先备份依赖来源
- 先把最关键模块稳住
- 新模块逐步减少对 GetX 的继续扩散
- 再考虑渐进迁移
因为状态管理迁移这种事,一旦上头,很容易把"风险治理"做成"二次事故"。
最后
这次 get 的事情,对我来说最大的提醒,不是"某个包危险",而是另一件更本质的事:
技术选型从来不是一锤子买卖。
你今天选一个方案,不只是选它今天写起来爽不爽, 你其实是在选:
- 三个月后它还好不好改
- 半年后新人能不能接
- 一年后团队还能不能稳稳维护
- 真出事时,你们有没有兜底能力
所以如果你现在问我:
"Get 这波之后,Flutter 状态管理该怎么重新看?"
我的答案是:
- Provider,适合入门和轻量场景
- Riverpod,是我目前最愿意推荐给新项目的平衡方案
- Cubit / Bloc,适合复杂业务和多人协作
- GetX,不是不能用,但以后别再只看"写起来爽不爽"了
说到底,真正好的状态管理方案,不是"最酷"的那个。
而是当项目做大之后,它还能让你回答清楚下面这句话:
"状态从哪来,为什么变,谁在监听,副作用在哪发生。"
如果这个问题它还能帮你解释清楚,那它就值钱。 如果它让这些东西越来越糊,那它迟早会反噬你。
这也是我这两天重新看 Flutter 状态管理生态之后,最真实的感受。
如果你们团队现在也在重新评估状态管理路线,希望这篇能帮你少踩几个坑。
如果你想让我继续写
这篇如果大家爱看,后面我可以继续写三篇:
- Provider、Riverpod、Bloc,我做了一个同业务 Demo,带你看真实代码差异
- GetX 老项目怎么渐进式迁移,不推倒重来
- Flutter 状态管理怎么选,别只看 API,得看团队结构
如果你觉得有用,评论区告诉我,我就继续更。
往期文章回顾
Get 删库风波
Web 前端转 Flutter
Flutter 图片编辑器
- 掘金文章 (juejin.cn/post/757106...)
- pubdev (pub.dev/packages/fl...)
- github (github.com/xinqingaa/f...)
Flutter 全链路监控 SDK
- 掘金文章 (juejin.cn/post/756497...)
- pubdev (pub.dev/packages/fl...)
- github (github.com/xinqingaa/f...)