文章

GetX 框架对比深度解析

GetX 是「全家桶」式方案:状态管理、路由、依赖注入一把梭。 对比它与其他状态管理库的取舍,掌握后能答出「GetX 为什么争议大、什么项目适合用」。

GetX 框架对比深度解析

一句话概括

GetX 是 Flutter 生态里最 “贪心” 的状态管理方案——它一个人干了 Provider + Navigator 2.0 + GetIt 三个库的活,代价是全局单例依赖和社区身份分裂。用它的感觉像开快车:爽是真爽,翻车也是一瞬间。

核心知识点

1. .obs + Obx:两行代码搞定响应式

1
2
3
4
5
6
7
8
9
class CounterController extends GetxController {
  final count = 0.obs;  // .obs 把 int 升级为 RxInt

  void increment() => count.value++;
}

// UI 层
final ctrl = Get.put(CounterController());
Obx(() => Text('${ctrl.count.value}'));  // 自动监听 count 变化

Provider 要写 ChangeNotifier + notifyListeners + Consumer,BLoC 要写 Event + State + Bloc + BlocBuilder。GetX 只需要 .obs + Obx。

2. 三种依赖注入方式,生命周期各不相同

1
2
3
4
5
6
7
8
// Get.put:立即创建,全局存活(相当于单例)
Get.put(UserController());

// Get.lazyPut:首次 Get.find() 时才创建
Get.lazyPut(() => UserController());

// Get.create:每次 Get.find() 都新建,页面销毁时自动释放
Get.create(() => FormController());

面试关键:Get.put 和 Get.lazyPut 的实例在 GetMaterialApp 生命周期内不会被自动回收,但可以通过 Get.delete<T>() 手动销毁。Get.create 的实例是页面级生命周期。

3. 路由三剑客:to / off / offAll

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Get.to(DetailPage());           // Navigator.push
Get.off(LoginPage());           // Navigator.pushReplacement
Get.offAll(HomePage());         // Navigator.pushAndRemoveUntil

// 带参数 + 接收
Get.to(DetailPage(), arguments: {'id': 42});
final args = Get.arguments;  // {'id': 42}

// 路由中间件:页面跳转前的拦截器
class AuthMiddleware extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    if (!Get.find<AuthController>().isLoggedIn) {
      return const RouteSettings(name: '/login');
    }
    return null;
  }
}

4. 全局工具:SnackBar / Dialog / BottomSheet 一行搞定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 不需要 BuildContext,不需要 ScaffoldMessenger
Get.snackbar('成功', '保存完毕',
  snackPosition: SnackPosition.TOP,
  backgroundColor: Colors.green,
);

Get.defaultDialog(
  title: '确认删除',
  middleText: '此操作不可恢复',
  textConfirm: '删除',
  onConfirm: () => Get.back(),
);

Get.bottomSheet(
  SizedBox(height: 200, child: Column(children: [
    ListTile(title: Text('拍照'), onTap: () => Get.back()),
    ListTile(title: Text('从相册选择'), onTap: () => Get.back()),
  ])),
);

5. Obx vs GetBuilder:两个更新机制的区别

1
2
3
4
5
6
7
8
9
// Obx:自动追踪 Rx 变量依赖,变量一变就重建
Obx(() => Text('${ctrl.count.value}'));

// GetBuilder:手动调用 update() 才重建,性能更可控
class MyCtrl extends GetxController {
  int count = 0;
  void inc() { count++; update(); }
}
GetBuilder<MyCtrl>(builder: (ctrl) => Text('${ctrl.count}'));

Obx 用在频繁变化的场景(表单、实时数据),GetBuilder 用在偶尔变化的场景(列表排序、筛选切换)。

其实你每天都在用

  • 表单的提交按钮状态 — 几个输入框都是 TextEditingController 配对 Obx,按钮跟着自动 disabled / enabled
  • 页面切换动画 — GetX 内置了 Get.to(() => Page(), transition: Transition.fadeIn),省去自己写 PageRouteBuilder
  • 全局 Loading 遮罩 — Get.showOverlay() 不需要 BuildContext,从 Service 层就能弹出 loading
  • 深色模式切换 — ThemeController 里一个 isDark.obs,所有页面 Obx 自动生效
  • 依赖注入的 Service 层 — Get.lazyPut(() => ApiService()),任何地方 Get.find<ApiService>() 就能拿到

常见误解(FAQ)

  • ❌ 误区:「GetX 是高性能的代表」 Obx 的自动追踪依赖在大量 Widget 时反而有开销——每次 builder 执行都要遍历所有访问的 Rx 变量来注册依赖。简单场景看不出,一个页面几百个 Obx 时可能会出现重建风暴。

  • ❌ 误区:「GetMaterialApp 和 MaterialApp 完全一样」 GetMaterialApp 是 MaterialApp 的子类,额外挂载了全局路由栈、SnackBar 队列、Binding 系统。如果你只用了 GetX 的依赖注入功能,MaterialApp 也够用;但一旦用了 Get.to(),就必须上 GetMaterialApp。

  • ❌ 误区:「Get.find() 不会返回 null」 Get.find 在实例不存在时直接抛异常,不是返回 null。这比 Provider 的 “ProviderNotFoundException” 更隐蔽——编译时不检查,运行到那一行才炸。生产环境记得用 Get.findOrNull<T>()。

  • ❌ 误区:「GetX 用全局单例,所以不安全」 单例不是原罪。但 GetX 的单例没有类似于 Riverpod 的 override 机制——测试时无法用 mock 实例替换生产实例。这是 GetX 测试体验的最大短板。

一句话总结

GetX 像瑞士军刀——一个工具解决路由、DI、状态三件事,中小项目开发效率拉满。但在大项目中,它的全局单例和紧耦合设计会让重构变成噩梦。用之前想清楚:你是在写一个 Demo 还是一个需要活五年的产品。

本文由作者按照 CC BY 4.0 进行授权