GetX 之死 | 8 年从未用过,以后将不会再用

1.一个时代的句号

2026 年的某一天,当你打开 https://github.com/jonataslaw/getx,迎接你的不是那个熟悉的 10000+ star 的仓库,而是一个冰冷的 404 页面。

不只是仓库。作者 jonataslaw(Jonny Borges)的整个 GitHub 主页也 404 了。get_cliget_serverget_storage------整个 GetX 生态的所有仓库,一夜之间全部消失。

没有告别信,没有迁移公告,没有"项目已归档"的标注。一个曾经是 Flutter 社区最具争议、也最广泛使用的第三方包,就这样无声无息地消失了。

pub.dev 上的 get 包还在------因为 pub.dev 是独立托管的,不会因为 GitHub 删库而下架。你的项目今天还能 flutter pub get,还能编译,还能运行。但源码没了,issue 没了,786 个未关闭的 issue 没了,78 个待合并的 PR 没了,文档没了。

一个没有源码仓库的包,和一具没有灵魂的躯壳有什么区别?


2.回顾:GetX 曾经有多火

在说"为什么不用"之前,先承认一个事实:GetX 确实火过。并且号称宇宙第一:

  • GitHub 10500+ stars,1600+ forks
  • pub.dev 上曾经是 likes 数最高的 Flutter 包之一
  • README 被翻译成十几种语言(日语、韩语、阿拉伯语、葡萄牙语......)
  • 大量教程、视频、课程围绕 GetX 构建
  • 在中文 Flutter 社区尤其流行,几乎每个技术群都有人推荐

它火是有原因的。看这段代码:

dart 复制代码
// 传统方式:定义 StatefulWidget,写 setState,管理生命周期
class CounterPage extends StatefulWidget {
  @override
  _CounterPageState createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(child: Text('$count')),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => count++),
        child: Icon(Icons.add),
      ),
    );
  }
}
dart 复制代码
// GetX 方式:三行搞定
final count = 0.obs;

Obx(() => Text('$count'));

onPressed: () => count++;

对于刚入门 Flutter 的开发者来说,这种简洁性是致命的吸引力。"为什么要写那么多样板代码?GetX 三行就搞定了。"

但简洁和简单不是一回事。简洁是表面上代码少,简单是底层复杂度低。GetX 的代码少,但底层复杂度一点都不低------它只是把复杂度藏起来了。


3.我为什么 8 年从未用过 GetX

从 2018 年开始写 Flutter,到现在 8 年了。期间无数次被推荐 GetX,无数次在技术群里看到"用 GetX 三行代码搞定"的安利。但我一次都没用过。甚至我的群聊公告里都写着:

本群禁止讨论 GetX,有任何问题可以向 GetX 官方提issue。

不是因为我有先见之明,而是因为它违反了我认为正确的几个原则。

原则一:不要把所有鸡蛋放在一个篮子里

GetX 管了你的状态、路由、依赖注入、国际化、主题、HTTP 请求、本地存储、表单验证......一个包解决所有问题,听起来很美。

但软件工程的基本常识是:耦合越紧,风险越大。 来看一个真实的对比:

yaml 复制代码
# 方案 A:每层独立
dependencies:
  provider: ^6.0.0        # 状态管理
  go_router: ^14.0.0      # 路由
  dio: ^5.0.0             # 网络请求
  shared_preferences: ^2.0.0  # 本地存储
  intl: ^0.19.0           # 国际化

# 方案 B:全家桶
dependencies:
  get: ^4.7.3             # 状态 + 路由 + DI + 网络 + 存储 + 国际化 + ...

方案 A 看起来依赖多,但每个依赖都是独立的。dio 不维护了?换成 http 包,其他四个完全不受影响。路由库想升级?只改路由层,状态管理一行不用动。

方案 B 看起来清爽,只有一个依赖。但这个依赖出问题 = 所有东西出问题。

今天,这个"所有东西出问题"的场景真的发生了。

原则二:隐式依赖是技术债的温床

GetX 最大的卖点之一是"不需要 context"。Get.find() 可以在代码的任何角落调用,不需要 Widget 树,不需要构造函数参数。

dart 复制代码
// GetX 的方式:在任何地方都能拿到任何东西
class OrderController extends GetxController {
  void submitOrder() {
    // 这三个依赖从哪来的?谁注册的?什么时候注册的?生命周期是什么?
    final cart = Get.find<CartController>();
    final auth = Get.find<AuthController>();
    final api = Get.find<ApiService>();
    // ...
  }
}
dart 复制代码
// 官方推荐的方式:依赖通过构造函数显式传入
class OrderViewModel extends ChangeNotifier {
  final CartRepository _cartRepository;
  final AuthRepository _authRepository;

  // 一眼就知道依赖了什么,测试时直接传 Fake
  OrderViewModel({
    required CartRepository cartRepository,
    required AuthRepository authRepository,
  }) : _cartRepository = cartRepository,
       _authRepository = authRepository;
}

写起来确实多了几行。但代价是什么?

维度 Get.find() 构造函数注入
知道一个类依赖了什么 全局搜索才知道 看构造函数就知道
测试时 Mock 依赖 配置 GetX 全局容器 直接传 Fake 对象
删了一个注册 编译通过,运行时崩 编译直接报错
重构时追踪依赖 全局搜索 Get.find<T>() IDE 的"Find Usages"
循环依赖检测 运行时才发现 编译时就报错

这不是"风格偏好",这是编译时安全 vs 运行时崩溃 的根本差异。一个项目有 50 个 Controller,每个都用 Get.find() 拿依赖,你敢重构吗?

原则三:不要依赖个人英雄主义

GetX 本质上是一个人的项目。jonataslaw 一个人写了状态管理、路由、依赖注入、HTTP 客户端、国际化、主题......一个人维护这么大的范围,质量和持续性都无法保证。

早在删库之前,信号就已经很明显了:

  • 2023 年 6 月:社区开始问"5.0 什么时候发布?"(Issue #2797),至今没有正式版
  • 2023-2024 年:超过 13 个月没有版本更新,停留在 4.6.5
  • 2025 年 1 月:有人开了 Issue #3295 直接问"GetX 还活着吗?"
  • 786 个未关闭的 issue,78 个待合并的 PR------一个人根本处理不过来

对比一下官方推荐的方案:

维护者 会因为一个人的决定消失吗?
provider Flutter 官方团队 不会
go_router Flutter 官方团队 不会
ChangeNotifier Flutter 框架内置 不会
Navigator Flutter 框架内置 不会
flutter_bloc 社区团队(多人维护) 极低概率
riverpod Remi Rousselet(provider 作者) 低概率,且有社区 fork 能力
GetX jonataslaw(一个人) 已经发生了
原则四:"不需要 context"不是优点,是危险信号

GetX 的宣传语之一是"不需要 context 就能导航、弹对话框、显示 SnackBar"。

dart 复制代码
// GetX:不需要 context
Get.to(HomePage());
Get.snackbar('标题', '内容');
Get.dialog(AlertDialog(...));

听起来很方便。但 context 在 Flutter 中不是累赘------它是 Widget 树的定位器,告诉框架"我在哪里"。绕过 context 意味着绕过 Flutter 的 Widget 生命周期管理,这会导致:

  • SnackBar 在页面已经销毁后还在显示
  • 对话框在错误的路由上弹出
  • 内存泄漏------Controller 没有跟随 Widget 生命周期释放

GetX 用全局状态和静态方法绕过了 Flutter 的设计,短期内写起来爽,长期维护时各种幽灵 Bug 让你怀疑人生。


4.给正在用 GetX 的项目的建议

如果你的项目正在用 GetX,不要恐慌,但也不要心存侥幸。

短期内你是安全的:

  • pub.dev 上的包不会因为 GitHub 删库而消失
  • 你的 pubspec.lock 锁定了版本,flutter pub get 还能正常工作
  • 已编译的应用不受任何影响

但定时炸弹已经埋下了:

  • Flutter 下一个大版本更新(比如 Dart 4.0)可能导致 GetX 不兼容,没有人会修
  • 发现 Bug 没有人修,发现安全漏洞没有人补
  • 新入职的同事看到一个 404 的仓库链接,会对项目的技术选型产生严重怀疑
迁移路径

不要一次性重写。逐层替换,每次只动一个模块,每次都跑测试:

scss 复制代码
第一步:路由(影响范围最小,最先动手)
  Get.toNamed('/home')  →  GoRouter 的声明式路由
  Get.back()            →  context.pop()
  GetPage              →  GoRoute

第二步:依赖注入(把隐式改成显式)
  Get.put(MyController())     →  ChangeNotifierProvider(create: (_) => MyViewModel())
  Get.find<MyController>()    →  context.read<MyViewModel>()
  Get.lazyPut(...)            →  Provider 的 lazy 默认行为

第三步:状态管理(工作量最大,收益也最大)
  GetxController + .obs + Obx  →  ChangeNotifier + Consumer / context.watch
  update()                     →  notifyListeners()
  GetBuilder                   →  Consumer

第四步:其他零散功能
  GetConnect        →  dio / http
  GetX 国际化       →  flutter_localizations + ARB 文件
  GetX 主题切换     →  ThemeData + ThemeMode
  Get.snackbar()    →  ScaffoldMessenger.of(context).showSnackBar()
  Get.dialog()      →  showDialog(context: context, ...)

每完成一步,跑一遍测试,确认没有回归。如果你的项目没有测试------这是另一个需要补的债,而且优先级比迁移 GetX 更高。

迁移的工作量评估
项目规模 GetX 使用深度 预估迁移时间
小项目(10-20 个页面) 只用了状态管理 1-2 周
中项目(30-50 个页面) 状态 + 路由 + DI 2-4 周
大项目(100+ 个页面) 全家桶深度使用 1-3 个月

如果是大项目,建议新功能用新方案写,老功能逐步迁移,不要停下业务开发来专门做迁移。

5.更深层的教训

GetX 之死不是个案。它揭示了开源生态中一个结构性的风险:社区的繁荣可以掩盖项目的脆弱性。

10000+ star、几十种语言的 README 翻译、数百个教程视频------这些都是社区繁荣的表现。但项目的健康度不取决于 star 数,而取决于:

  • 核心维护者有几个人?(GetX:1 个)
  • 有没有组织/公司背书?(GetX:没有)
  • issue 响应速度如何?(GetX:786 个未关闭)
  • 版本发布频率如何?(GetX:5.0 难产三年)

下次选择一个第三方包时,不要只看 star 数和 likes。打开 GitHub 仓库,看看:

  1. Contributors 页面------核心贡献者超过 3 个人吗?
  2. 最近的 commit------最后一次提交是什么时候?
  3. Issue 列表------维护者有在回复吗?
  4. Release 历史------版本发布有规律吗?

如果一个包只有一个核心维护者、半年没有 commit、几百个未关闭的 issue------不管它有多少 star,都要三思。

5. 2026 年,Flutter 状态管理该怎么选

GetX 退场后,Flutter 状态管理的格局更清晰了:

方案 定位 适合谁 维护状况
provider + ChangeNotifier Flutter 官方推荐的入门方案 大多数项目 官方维护
Riverpod provider 的进化版,编译时安全 追求类型安全和可测试性的项目 社区活跃,多人维护
flutter_bloc / Cubit 企业级方案,严格的单向数据流 大型项目、需要审计追踪的场景 社区活跃,多人维护
setState 最简单的方式 临时状态、原型开发 框架内置,永远不会消失

没有 GetX。以后也不会有 GetX。

6. 写在最后

8 年前我选择不用 GetX,不是因为预见了今天。是因为几个朴素的判断:

  • 一个人维护的全家桶,风险太高
  • 隐式依赖写起来爽,维护起来痛
  • 绕过框架设计的"便利",迟早要还债

今天这些判断被验证了。但我并不觉得高兴------毕竟有大量的项目和开发者受到了影响。那些用 GetX 写了几万行代码的团队,现在面对的是一个没有源码、没有维护者、没有未来的核心依赖。

如果你正在选择 Flutter 的技术栈,记住一条原则:

每一层都应该可以独立替换。

状态管理、路由、依赖注入、网络请求------每一层用独立的方案,每一层都有替代品。这样无论哪一层出问题,你都只需要换那一层,而不是重写整个应用。

这不是过度设计,这是基本的风险管理。

GetX 教会了我们:在开源世界里,便利是借来的,风险是自己的。


笔者目前正在公众号 编程之王 编写并发布《Flutter Agent Skills 全解析》系列,覆盖 Flutter 官方推荐的 22 个开发技能。如果你对 Flutter 官方推荐的架构、状态管理、测试等最佳实践感兴趣,欢迎关注。

相关推荐
「、皓子~2 小时前
海狸IM技术升级:从Uniapp到Flutter的跨平台重构之路
flutter·重构·golang·uni-app·im·社交软件
黑牛儿2 小时前
2026 MySQL 面试 100 题: 索引 / 事务 / 锁(答案 + 原理)
android·mysql·面试
励志的小陈2 小时前
数据结构--堆(C语言实现)
android·c语言·数据结构
冲浪中台2 小时前
20个常用的CSS知识点
前端·css
青枣八神2 小时前
如何让手机访问电脑本地的前端服务器网页(Vite等前端项目)
服务器·前端·web·手机访问
习惯就好zz2 小时前
RK3588 Android 12 修改 NTP 服务器:从资源覆盖到时间同步验证
android·运维·服务器·aosp·ntp
榴莲omega2 小时前
第14天:React 工程化与设计模式
前端·react.js·设计模式
FmZero2 小时前
后端全栈路线(9小时前端速成)
前端·vscode·学习
万世浮华戏骨2 小时前
Web 后端 Python 基础安全
前端·python·安全