GetX 之死 | 8 年從未用過,以後將不會再用

1. 一個時代的句點

2026 年的某一天,當你打開 https://github.com/jonataslaw/getx,迎接你的不是那個熟悉的 10000+ star 的倉庫,而是一個冰冷的 404 頁面。

image.png

不只是倉庫。作者 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 確實火過。並且號稱宇宙第一:

image.png

  • GitHub 10,500+ stars,1,600+ forks
  • 在 pub.dev 上曾經是 likes 數最高的 Flutter 套件之一
  • README 被翻譯成十幾種語言(日語、韓語、阿拉伯語、葡萄牙語……)
  • 大量教學、影片、課程圍繞 GetX 建構
  • 在中文 Flutter 社群尤其流行,幾乎每個技術群都有推薦

它火是有原因的。看這段程式:

// 傳統方式:定義 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),
      ),
    );
  }
}
// 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 請求、本地儲存、表單驗證……一個套件解決所有問題,聽起來很美。

但軟體工程的基本常識是:耦合越緊,風險越大。來看一個真實的對比:

# 方案 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 樹,不需要建構式參數。

// GetX 的方式:在任何地方都能拿到任何東西
class OrderController extends GetxController {
  void submitOrder() {
    // 這三個依賴從哪來的?誰註冊的?什麼時候註冊的?生命週期是什麼?
    final cart = Get.find<CartController>();
    final auth = Get.find<AuthController>();
    final api = Get.find<ApiService>();
    // ...
  }
}
// 官方推薦方式:依賴透過建構式顯式傳入
class OrderViewModel extends ChangeNotifier {
  final CartRepository _cartRepository;
  final AuthRepository _authRepository;

  // 一眼就知道依賴了什麼,測試時直接傳 Fake
  OrderViewModel({
    required CartRepository cartRepository,
    required AuthRepository authRepository,
  })  : _cartRepository = cartRepository,
        _authRepository = authRepository;
}

寫起來確實多了幾行。但代價是什麼?

  • 透過 Get.find():只有在執行時全域容器註冊好後才會成功,刪掉一個註冊也許編譯仍通過但執行時會崩潰
  • 透過建構式注入:可以從建構式一眼看出依賴,測試時可傳入 Fake,重構時 IDE 可追蹤引用,循環依賴可在編譯期或靜態分析中發現

這不是「風格偏好」,這是編譯時安全 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:有官方或大型貢獻者支持
  • ChangeNotifier:內建於 Flutter 框架
  • flutter_bloc:社群多人維護,風險低
  • riverpod:由 Remi Rousselet(provider 作者)主導,社群活躍

GetX 的情況是:核心維護者只有一人,且已經發生了倒閉式的消失。

原則四:「不需要 context」不是優點,是危險訊號

GetX 的宣傳語之一是「不需要 context 就能導航、彈對話框、顯示 SnackBar」。

// 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 的倉庫連結,會對專案的技術選型產生嚴重疑慮

遷移路徑

不要一次性重寫。逐層替換,每次只動一個模組,每次都跑測試:

  1. 路由(影響範圍最小,最先下手)

    • Get.toNamed('/home') → 使用 GoRouter 的宣告式路由
    • Get.back()context.pop()(或 Navigator.of(context).pop()
    • GetPageGoRoute
  2. 依賴注入(把隱式改成顯式)

    • Get.put(MyController())ChangeNotifierProvider(create: (_) => MyViewModel())
    • Get.find<MyController>()context.read<MyViewModel>()
    • Get.lazyPut(...) → Provider 的 lazy 行為或使用其他 DI 工具(如 get_it)
  3. 狀態管理(工作量最大,但收益也最大)

    • GetxController + .obs + ObxChangeNotifier + Consumer / context.watch
    • update()notifyListeners()
    • GetBuilderConsumer
  4. 其他零散功能

    • GetConnectdio / http
    • GetX 國際化 → flutter_localizations + ARB 檔案
    • GetX 主題切換 → ThemeData + ThemeMode
    • Get.snackbar()ScaffoldMessenger.of(context).showSnackBar()
    • Get.dialog()showDialog(context: context, ...)

每完成一步,就執行一次測試,確認沒有回歸。如果你的專案沒有測試——這是另一項需要補的債,而且優先度比遷移 GetX 還高。

遷移的工作量評估

  • 小專案(10–20 個畫面)、只用了狀態管理:1–2 週
  • 中型專案(30–50 個畫面)、狀態 + 路由 + DI:2–4 週
  • 大型專案(100+ 個畫面)、深度使用全家桶:1–3 個月

如果是大專案,建議新功能以新方案開發,舊功能逐步遷移,不要停下業務開發來專門做一次性全面遷移。


5. 更深層的教訓

GetX 之死不是個案。它揭示了開源生態中的一個結構性風險:社群的繁榮可以掩蓋專案的脆弱性。

10,000+ 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——不管它有多少 stars,都要三思。


6. 2026 年,Flutter 狀態管理該怎麼選

GetX 退場後,Flutter 狀態管理的格局更清晰了:

方案定位 適合誰 維護狀況
provider + ChangeNotifier Flutter 官方推薦的入門方案 大多數專案,官方維護良好
Riverpod provider 的進化版,編譯時安全 追求類型安全和可測試性的專案,社群活躍,多人維護
flutter_bloc / Cubit 企業級方案,嚴格的單向資料流 大型專案、需要稽核追蹤的場景,社群活躍
setState 最簡單的方式 臨時狀態、原型開發,框架內建,永遠不會消失

沒有 GetX,以後也不會再有 GetX(指那種由單一維護者承擔整個「全家桶」功能並且極度隱式依賴的情況)。


7. 寫在最後

8 年前我選擇不用 GetX,不是因為預見了今天,而是因為幾個樸素的判斷:

  • 一個人維護的全家桶,風險太高
  • 隱式依賴寫起來爽,維護起來痛
  • 繞過框架設計帶來的「便利」,遲早要還債

今天這些判斷被驗證了。但我並不覺得高興——畢竟有大量的專案和開發者受到影響。那些用 GetX 寫了數萬行程式的團隊,現在面對的是一個沒有原始碼、沒有維護者、沒有未來的核心依賴。

如果你正在選擇 Flutter 的技術棧,記住一條原則:

每一層都應該可以獨立替換。

狀態管理、路由、依賴注入、網路請求——每一層用獨立的方案,每一層都有替代品。這樣無論哪一層出問題,你只需要換那一層,而不是重寫整個應用。

這不是過度設計,這是基本的風險管理。

GetX 教會了我們:在開源世界裡,便利是借來的,風險是自己的。


筆者目前在公眾號「編程之王」撰寫並發布《Flutter Agent Skills 全解析》系列,涵蓋 Flutter 官方推薦的 22 個開發技能。如果你對 Flutter 官方推薦的架構、狀態管理、測試等最佳實踐感興趣,歡迎關注。


原文出處:https://juejin.cn/post/7628535637260959770


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝4   💬5   ❤️2
342
🥈
我愛JS
📝1   💬4   ❤️2
114
🥉
💬1  
4
#4
Gigi
2
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登