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

不只是仓库。作者 jonataslaw(Jonny Borges)的整个 GitHub 主页也 404 了。get_cli、get_server、get_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 仓库,看看:
- Contributors 页面------核心贡献者超过 3 个人吗?
- 最近的 commit------最后一次提交是什么时候?
- Issue 列表------维护者有在回复吗?
- 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 官方推荐的架构、状态管理、测试等最佳实践感兴趣,欢迎关注。