Get 这波之后,我把 Flutter 状态管理重新看了一遍:新项目到底该选谁?

前言

昨天 get 的删库跑路之后发,社区和公司部门内部基本可以说是原地炸了。(早上作者说是他的github账户被风控,但是get本身的问题已经很多了...)

问得最多的,不是"这个包现在还能不能装",而是另一类更扎心的问题:

"以后 Flutter 项目状态管理到底该怎么选?"

"Get 还能不能继续用?"

"Provider、Riverpod、Bloc 这些,到底谁更靠谱?"

"你如果现在开一个新项目,你会选谁?"

我仔细思考了,发现很多讨论都有两个毛病:

  1. 只聊 API,不聊项目演进后的维护成本。
  2. 只聊自己喜欢什么,不聊团队、业务、复杂度、学习成本这些现实问题。

所以这篇文章,我想和大家聊聊我的看法。

我想做的事情很简单:

借着这次 get 的风波,把 Flutter 生态里主流的状态管理方案,重新摆到桌面上,按真实项目的标准,认认真真聊一遍。

不是聊"哪个最优雅",而是聊:

  • 它到底解决什么问题
  • 它的边界在哪
  • 它为什么有人爱,也为什么有人骂
  • 如果现在重新开一个项目,我会怎么选

为了避免这篇文章变成"空对空",我还顺手把同一个业务场景做成了一个开源 Demo,把 Provider / Riverpod / Cubit / Bloc Event 全都落地了一遍。

开源地址在这里: state_manages

后面我文中提到的一些对比,不只是嘴上说说,基本都能在这个仓库里对上代码。

先说结论

我先把结论放前面,免得大家看半天最后发现和自己预想差不多。

如果你现在问我:

"2026 年这个时间点,Flutter 新项目状态管理怎么选?"

我的答案是:

  • 小项目、单人项目、快速起步:Provider
  • 中大型新项目、我个人最愿意推荐的平衡方案:Riverpod
  • 多人协作、复杂业务、强调规范和状态流可追踪:Cubit / Bloc
  • 老项目已经深度绑定 GetX:先稳住,不要一激动就全量重构
  • 新项目再从 0 开始选 GetX:我个人会明显更谨慎

注意,我这里不是说 GetX 技术上突然一夜归零了。

而是说,这次事情把一个以前很多人不愿意正视的问题,硬生生摊开了:

状态管理从来不是"代码写起来爽不爽"这么简单,它还关乎维护、协作、生态稳定性、升级路线、团队兜底能力。

说得再直白一点:

以前大家觉得"能跑就行", 这次很多人才开始意识到:

"哦,原来依赖生态稳定,也是技术选型的一部分。"


篇章一:先把问题掰直,状态管理到底在管什么

很多人聊状态管理,一上来就对比 API:

  • setState
  • notifyListeners
  • ref.watch
  • emit
  • Obx

但这其实是表象。

状态管理真正要解决的,不是"你用哪个函数刷新页面",而是下面这几件事:

  1. 状态放在哪里
  2. 状态变化后,谁来通知 UI
  3. 异步请求、空状态、错误状态怎么建模
  4. 页面越来越复杂之后,代码会不会开始失控
  5. 团队里第二个人、第三个人接手时,还看不看得懂

你会发现,一个状态管理方案,真正的价值,不在于它能不能写出页面。

因为大家都能写。

真正拉开差距的,是当业务变成这样时:

  • 页面有列表
  • 列表要刷新
  • 请求会失败
  • 有搜索
  • 有筛选
  • 有排序
  • 还有弹 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,再组合起来。

这会带来两个很现实的好处:

  1. 结构更清楚
  2. 重建范围更好控制

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

我不会建议你们一夜重构。

我的建议是:

  1. 先锁版本
  2. 先备份依赖来源
  3. 先把最关键模块稳住
  4. 新模块逐步减少对 GetX 的继续扩散
  5. 再考虑渐进迁移

因为状态管理迁移这种事,一旦上头,很容易把"风险治理"做成"二次事故"。


最后

这次 get 的事情,对我来说最大的提醒,不是"某个包危险",而是另一件更本质的事:

技术选型从来不是一锤子买卖。

你今天选一个方案,不只是选它今天写起来爽不爽, 你其实是在选:

  • 三个月后它还好不好改
  • 半年后新人能不能接
  • 一年后团队还能不能稳稳维护
  • 真出事时,你们有没有兜底能力

所以如果你现在问我:

"Get 这波之后,Flutter 状态管理该怎么重新看?"

我的答案是:

  • Provider,适合入门和轻量场景
  • Riverpod,是我目前最愿意推荐给新项目的平衡方案
  • Cubit / Bloc,适合复杂业务和多人协作
  • GetX,不是不能用,但以后别再只看"写起来爽不爽"了

说到底,真正好的状态管理方案,不是"最酷"的那个。

而是当项目做大之后,它还能让你回答清楚下面这句话:

"状态从哪来,为什么变,谁在监听,副作用在哪发生。"

如果这个问题它还能帮你解释清楚,那它就值钱。 如果它让这些东西越来越糊,那它迟早会反噬你。

这也是我这两天重新看 Flutter 状态管理生态之后,最真实的感受。

如果你们团队现在也在重新评估状态管理路线,希望这篇能帮你少踩几个坑。


如果你想让我继续写

这篇如果大家爱看,后面我可以继续写三篇:

  1. Provider、Riverpod、Bloc,我做了一个同业务 Demo,带你看真实代码差异
  2. GetX 老项目怎么渐进式迁移,不推倒重来
  3. Flutter 状态管理怎么选,别只看 API,得看团队结构

如果你觉得有用,评论区告诉我,我就继续更。

往期文章回顾

Get 删库风波

Web 前端转 Flutter

Flutter 图片编辑器

Flutter 全链路监控 SDK

相关推荐
我不是外星人24 分钟前
有了 Harness Engineering ,真的还需要研发工程师吗?
前端·后端·ai编程
candyTong30 分钟前
RTK 技术原理:一次典型会话里,80% 上下文是怎么省下来的
javascript·后端·架构
IT_陈寒3 小时前
JavaScript的闭包把我坑惨了,说好的内存会自动回收呢?
前端·人工智能·后端
Jackson__4 小时前
分享一个横向滚动案例,带悬停暂停,通用性很强
前端
MariaH4 小时前
git rebase的使用
前端
_柳青杨4 小时前
深入理解 JavaScript 事件循环
前端·javascript
阡陌Jony4 小时前
关于前端性能优化的一些问题:
前端
用户600071819105 小时前
【翻译】简化 TSRX
前端
唐某人丶5 小时前
从画架构图开始:架构分析与进阶指南
架构
IT乐手6 小时前
佛德角逼平西班牙,国足还有啥借口?
前端