文章目录
- 前言
- GetX使用建议
- 状态管理
- GetX快速上手
- GetX基本功能介绍
- **核心作用**
- **代码示例**
- **关键细节**
- **性能建议**
- 参考链接
前言
在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基本功能介绍
核心作用
-
自动订阅响应式变量
Obx
内部会监听其回调函数(() => Widget
)中使用的所有Rx
变量(如RxInt
、RxString
、RxList
等)。- 当这些变量发生变化时,
Obx
会自动触发包裹的组件重建。
-
局部刷新,性能优化
- 不同于
setState
会重建整个页面,Obx
只更新其包裹的组件,减少不必要的渲染开销。
- 不同于
-
简洁的语法
- 无需手动管理订阅或取消订阅(相比
GetBuilder
或StreamBuilder
,代码更简洁)。
- 无需手动管理订阅或取消订阅(相比
代码示例
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),),);}
}
关键细节
-
依赖的变量必须为
Rx
类型Obx
仅对.obs
创建的响应式变量生效(如0.obs
、"hello".obs
),普通变量无效。
-
作用域限制
Obx
只会监听其回调函数内部直接使用的Rx
变量。例如:Obx(() => Text("${controller.count.value}")); // 有效 // 错误!下面的写法不会监听 count: // var text = "Count: ${controller.count.value}"; // Obx(() => Text(text));
-
与
GetBuilder
的区别Obx
适用于响应式变量(Rx
),自动订阅变化。GetBuilder
需要手动调用update()
,适合非响应式状态管理。
性能建议
- 避免过度嵌套
Obx
:每个Obx
会独立订阅变量,嵌套过多可能影响性能。 - 复杂场景使用
GetX
或GetBuilder
:如果需要更精细的控制(如防抖、条件更新),可以考虑其他 GetX 工具。
通过 Obx
,GetX 实现了类似 Flutter 原生 ValueListenableBuilder
或 StreamBuilder
的功能,但代码更简洁且性能更优。
参考链接
- get_it: 不是GetX,据说和riverpod搭配起来很好用