Flutter之GetX框架的使用

发布于:2025-06-14 ⋅ 阅读:(23) ⋅ 点赞:(0)

前言

在Reddit上,诟病GetX的声音很多,主要是说它做的事情太多,不是单一功能组件,违反单一职责原则。容易被滥用,容易产生意大利面式的代码等等。他们推荐riverpod。GetX上一次版本更新时2021年了。

它想做的太多了: 一个试图做所有事情的库,虽然方便,但并不能给你架构带来多少灵活性。如果GetX突然在你某个DI用例或路由用例中失败了,你就会突然遇到库膨胀的问题,这很容易导致工程师们用“各种风格”来写解决方案。最终导致意大利面代码和难以追踪的bug。

它偏离了Flutter的Context模型: 因为GetX的“定位器”是静态可用的,不依赖于BuildContext,所以它们本质上很脆弱,并且违背了Flutter最初的设计原则。像渲染生命周期这样的东西,并没有像Provider或Riverpod那样被遵守。

我用过provider、GetX和Riverpod,而且到目前为止,Riverpod是状态管理中最容易测试和最简单的方法。把它和Get结合起来,你就能得到你所需要的一切,我觉得。

Provider是一个轻量级的状态管理框架,它基于InheritedWidget和ChangeNotifier实现,提供了简单而强大的方式来共享状态,并支持依赖注入,非常适合小型应用和初学者。

Riverpod作为Provider的升级版,提供了更强大的功能和更好的性能,同时保持了简洁性和易用性。它使用全新的架构,具备出色的依赖管理和异步处理能力,适合中大型应用和有一定经验的开发者。

GetX则是一个快速、轻量级的状态管理和路由管理库,它提供了许多便利的功能,如依赖注入、路由管理、国际化、主题切换等。GetX的语法简洁且性能优秀,非常适合构建中小型应用,能够简化开发流程。

此外,Redux、MobX和BLoC也是Flutter中常见的状态管理框架。Redux通过单一的状态存储库管理状态,MobX基于响应式编程,而BLoC则基于Reactive Programming和Stream。

GetX使用建议

在某些地方,GetX 非常灵活(太灵活了),而且容易被误用。如果你没有好好考虑如何正确使用它,你会遇到很多问题。

这里有几条可以帮助你的规则:

  • 让你的模型和服务层独立于 GetX。如果你需要在那个部分进行依赖注入,使用其他库。

  • 只在表现层使用 GetX 的可观察对象和依赖管理(例如 Get.put, Get.find)。表现层是页面控制器和小部件。

  • 只在控制器中使用 GetX 的导航函数(例如 Get.to, Get.back)。避免在小部件中使用它们,尤其是在你的模型和服务层中。

  • GetX 包含很多快捷函数,比如 Get.height,不要使用它们,使用标准的 Flutter 等效函数。在这种情况下 MediaQuery.of(context).size.height

  • 尽量避免使用 GetX 的实用程序来显示对话框、提示条和底部弹出框。但如果你想使用它们,只能从控制器中调用它们。

  • 但是,你也有很大几率会遇到 GetX 路由的问题。它不够灵活,而且有点 bug。如果你想使用另一个路由器,你需要很好地理解 GetX 的工作原理。因为标准路由器管理控制器的生命周期。你需要用外部路由器实现类似的东西。

  • GetX 和 Riverpod 可以无缝混合使用,不会产生冲突。混合使用这两种框架的关键在于职责分离,让每个框架处理自己擅长的部分。通过 GetX 来处理 UI 层的状态和路由,同时使用 Riverpod 来管理复杂的全局状态或业务逻辑,可以充分发挥两者的优势。当你需要一个简洁的 UI 层管理方案并且同时需要处理复杂的状态逻辑时,混合使用这两者是一种不错的选择。

GetX 状态管理框架中,使用 Obx 包裹一个组件的主要作用是 自动监听响应式变量(Rx)的变化,并在变量值更新时自动重建该组件,从而实现高效的局部刷新,避免不必要的全局 build


状态管理

不用状态管理框架时,直接在Statefulwidget中定义状态类,里面持有所有数据,然后在事件处理中调用HTTP请求获取数据,在setState()刷新界面。但这种方式在跨组件共享状态时就不好处理了。于是有了几大状态管理框架。
在这里插入图片描述
再摆一个InheritedWidget的流程图吧:
在这里插入图片描述
InheritedWidget对子节点的Element,有个强大的操作功能:可以将子widget的element实例,储存在自身的InheritedElement中的_dependents变量中调用其notifyClients方法,会遍历_dependents中的子Element,然后调用子Element的markNeedsBuild方法,就完成了定点刷新子节点的操作。

GetX快速上手

  • 代码拆分为View, Controller和Http请求三大部分,当然还有model
  • Controller统一管理所有数据,负责Http交互
  • view只管渲染Rx数据,用Obx包裹需要刷新的小部件,注意粒度尽可能小
  • 有几个Http请求,就在controller中定义几个Future,通过FutureBuilder来触发Http请求,不要自己在initState()显示调用

GetX基本功能介绍

核心作用

  1. 自动订阅响应式变量

    • Obx 内部会监听其回调函数(() => Widget)中使用的所有 Rx 变量(如 RxIntRxStringRxList 等)。
    • 当这些变量发生变化时,Obx 会自动触发包裹的组件重建。
  2. 局部刷新,性能优化

    • 不同于 setState 会重建整个页面,Obx 只更新其包裹的组件,减少不必要的渲染开销。
  3. 简洁的语法

    • 无需手动管理订阅或取消订阅(相比 GetBuilderStreamBuilder,代码更简洁)。

代码示例

class CounterController extends GetxController {
  var count = 0.obs; // 声明响应式变量
}

class MyPage extends StatelessWidget {
  final controller = Get.put(CounterController()); // 注入控制器

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Obx(
          () => Text("Count: ${controller.count.value}"), // 自动监听 count 变化
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => controller.count++, // 修改 count 会触发 Obx 更新
        child: Icon(Icons.add),
      ),
    );
  }
}

关键细节

  1. 依赖的变量必须为 Rx 类型

    • Obx 仅对 .obs 创建的响应式变量生效(如 0.obs"hello".obs),普通变量无效。
  2. 作用域限制

    • Obx 只会监听其回调函数内部直接使用的 Rx 变量。例如:
      Obx(() => Text("${controller.count.value}")); // 有效
      // 错误!下面的写法不会监听 count:
      // var text = "Count: ${controller.count.value}";
      // Obx(() => Text(text));
      
  3. GetBuilder 的区别

    • Obx 适用于响应式变量(Rx),自动订阅变化。
    • GetBuilder 需要手动调用 update(),适合非响应式状态管理。

性能建议

  • 避免过度嵌套 Obx:每个 Obx 会独立订阅变量,嵌套过多可能影响性能。
  • 复杂场景使用 GetXGetBuilder:如果需要更精细的控制(如防抖、条件更新),可以考虑其他 GetX 工具。

通过 Obx,GetX 实现了类似 Flutter 原生 ValueListenableBuilderStreamBuilder 的功能,但代码更简洁且性能更优。

参考链接

  • get_it: 不是GetX,据说和riverpod搭配起来很好用