目录
1. 引言
在 Flutter 开发中,状态管理是一个至关重要的概念。它决定了应用的性能和维护性。GetX 是一个强大的 Flutter 包,它提供了高效、简洁的状态管理、依赖管理和路由管理功能。本文将深入探讨 GetX 的状态管理,并展示如何在 Flutter 应用中使用它。
Flutter也有其它的状态管理器,它们有着各自的特点:
- BLoC非常安全和高效,但是对于初学者来说非常复杂,这使得人们无法使用Flutter进行开发。
- MobX比BLoC更容易,而且是响应式的,几乎是完美的,但是你需要使用一个代码生成器,对于大型应用来说,这降低了生产力,因为你需要喝很多咖啡,直到你的代码在
flutter clean
之后再次准备好(这不是MobX的错,而是codegen真的很慢!)。 - Provider使用InheritedWidget来传递相同的监听器,以此来解决上面报告的ChangeNotifier的问题,这意味着对其ChangeNotifier类的任何访问都必须在widget树内。
Get并不是比任何其他状态管理器更好或更差,而是说你应该分析这些要点以及下面的要点来选择是只用Get,还是与其他状态管理器结合使用。Get不是其他状态管理器的敌人,因为Get是一个微框架,而不仅仅是一个状态管理器,它的状态管理功能既可以单独使用,也可以与其他状态管理器结合使用。
2.安装和配置 GetX
要在 Flutter 项目中使用 GetX,需要在 pubspec.yaml 文件中添加 GetX 依赖:。
dependencies:
get: ^4.6.6
然后,在需要使用 GetX 的文件中导入它:
import ‘package:get/get.dart’;
3.GetX 状态管理的核心概念
1.响应式编程
GetX 的状态管理基于响应式编程,这意味着状态的变化会自动更新依赖该状态的 UI。GetX 通过 Rx 类及其子类(如 RxInt、RxString)实现了响应式编程。
class MyController extends GetxController {
var count = 0.obs;void increment() {
count++;
}
}
在上面的代码中,count 是一个可观察的整数 (RxInt)。当 count 的值改变时,任何依赖它的 UI 会自动更新。
2.控制器 (Controller)
控制器用于管理状态和业务逻辑。它继承自 GetxController,可以在其中定义可观察变量和方法。
class MyController extends GetxController {
var count = 0.obs;void increment() {
count++;
}
}
在这个例子中,MyController 管理 count 状态和 increment 方法。
3.状态 (State)
状态是应用程序的当前数据或信息。在 GetX 中,可以使用 .obs 修饰符来声明可观察变量,并使用 update() 方法手动触发更新。
class MyController extends GetxController {
var count = 0.obs;void increment() {
count++;
}
}
通过 count 的 .obs 修饰符,count 变成了一个可观察变量。
4.状态更新(State Update)
GetX 提供了多种更新状态的方法,包括 GetBuilder、GetX 和 Obx。
GetBuilder:非反应式,只在 update() 调用时更新。
GetX 和 Obx:反应式,只在依赖的变量变化时更新。
1class HomePage extends StatelessWidget {final MyController controller = Get.put(MyController()); Widget build(BuildContext context) { appBar: AppBar(title: Text('GetX Example')), mainAxisAlignment: MainAxisAlignment.center,return Text('Count: ${controller.count}'); onPressed: controller.increment, child: Text('Increment'),
5. 基本用法示例
1.Obx
以下图的效果为例。我们有两个变量,counter1,counter2和sum.其中sum = counter1+counter2.当我们更新counter1或者Counter2其中的一个变量,sum变量就可自动更新。
图1.计时器
我们看看如何实现。
首先我们把变量生成Getx的Rx类型。
下面这个三种写法都是Getx支持的,您可以任选其中一种您喜欢的方式。
1final count = Rx<int>(0);
然后再使用到该变量的Widget外面使用它Obx包起来即可。
完整代码如下:
1import 'package:flutter/material.dart';import 'package:get/get.dart';class ObxDemosPage extends StatelessWidget { ObxDemosPage({super.key});final count = Rx<int>(0); Widget build(BuildContext context) { title: const Text('ObX Demos'), mainAxisAlignment: MainAxisAlignment.spaceEvenly, Obx(()=> Text('counter1:${counter1.value.toInt()}'),), child: const Text('counter1++'),const SizedBox(height: 16.0), mainAxisAlignment: MainAxisAlignment.spaceEvenly, Obx(()=> Text('counter2:${counter2.value.toInt()}'),), child: const Text('counter2++'),const SizedBox(height: 16.0), Obx(()=> Text('sum:${counter2.value.toInt()+counter1.value.toInt()}'),),
2.GetX
还是上述图1的功能为例,我们还可以使用GetxController来实现。
顾名思义,controller也就是控制器的意思,这个类的出现是为了减少代码的低耦合。
我们把业务代码放到GetxController中,减少Widget部分的代码。
我们定义一个继承自GetxController的控制器,代码如下:
1class Controller extends GetxController {int get sum => count1.value + count2.value;
然后我们使用GetX包在Widget的外层,即可对变量的精准控制。
1class GetXCounterPage extends StatelessWidget { GetXCounterPage({super.key});final Controller controller = Get.put(Controller()); Widget build(BuildContext context) { title: const Text('计时器Demo'), mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceEvenly,print("count 1 rebuild");return Text('count1: ${controller.count1.value}'); onPressed: controller.incrementCount1, child: const Text('Increment Count1'), mainAxisAlignment: MainAxisAlignment.spaceEvenly, debugPrint("count 2 rebuild");return Text('count2: ${controller.count2.value}'); onPressed: controller.incrementCount2, child: const Text('Increment Count2'), debugPrint("sum rebuild");return Text('sum: ${controller.sum}');
上述的过程是自动的,也就是当我们调用GexController中的方法之后,被观察的值立即改变。
3.GetBuilder
GetBuilder的用法和GetX基本相同。区别就是如果使用GetBuilder必须手动的调用GetxController的update方法页面才会刷新。
以下图为例,我们我们的控制器有一个count变量,代码如下:
1class GetBuilderController extends GetxController {
当我们点击按钮之后,调用incrementCount方法,count的值并不会马上更新,只有手动的调用update方法,count才会更新。
完整的代码如下:
1import 'package:flutter/material.dart';import 'package:get/get.dart';class GetBuilderController extends GetxController {class GetBuilderDemosPage extends StatelessWidget { GetBuilderDemosPage({super.key});final GetBuilderController controller = Get.put(GetBuilderController()); Widget build(BuildContext context) { title: const Text('计时器Demo'), mainAxisAlignment: MainAxisAlignment.start, GetBuilder<GetBuilderController>( debugPrint("count rebuild");return Text('count: ${controller.count.value}'); onPressed: controller.incrementCount, child: const Text('increment'), onPressed: controller.update, child: const Text('update'),
图2.GetBuilder
4.Workers
在 GetX 中,Workers 是一种监控和响应 Rx 类型变量变化的机制。它们可以在变量变化时执行特定的回调函数,帮助开发者简化逻辑处理和状态更新。Workers 包括以下几种类型:
ever、everAll、once、interval、debounce
1.ever
ever 会在指定的 Rx 变量每次变化时调用回调函数。
1class Controller extends GetxController {int get sum => count1.value + count2.value; debugPrint("Count changed: $count2");
2.everAll
everAll 会在多个指定的 Rx 变量中的任意一个变化时调用回调函数。
1class MyController extends GetxController { everAll([count1, count2], (_) {print("One of the counts changed: count1 = $count1, count2 = $count2");
3.once
once 只会在指定的 Rx 变量第一次变化时调用回调函数。
1class MyController extends GetxController {print("Count changed for the first time: $count");
4.interval
interval 会在指定的 Rx 变量变化时调用回调函数,但在指定时间内只会调用一次,适用于减少频繁更新的场景。
1class MyController extends GetxController {print("Count changed (with interval): $count"); }, time: Duration(seconds: 1));
5.debounce
debounce 会在指定的 Rx 变量变化后的一段时间内没有再次变化时调用回调函数,适用于处理用户输入等场景。
1import 'package:flutter/material.dart';import 'package:get/get.dart';class MyController extends GetxController {print("Count1 changed: $count1"); everAll([count1, count2], (_) {print("Count1 or Count2 changed: count1 = $count1, count2 = $count2");print("Count1 changed for the first time: $count1");print("Count1 changed (with interval): $count1"); }, time: Duration(seconds: 1));print("Count1 changed (debounced): $count1"); }, time: Duration(seconds: 1));class MyApp extends StatelessWidget { Widget build(BuildContext context) {class HomeScreen extends StatelessWidget {final MyController controller = Get.put(MyController()); Widget build(BuildContext context) { title: Text('GetX Workers Example'), mainAxisAlignment: MainAxisAlignment.center, Obx(() => Text('Count1: ${controller.count1}')), Obx(() => Text('Count2: ${controller.count2}')), onPressed: controller.incrementCount1, child: Text('Increment Count1'), onPressed: controller.incrementCount2, child: Text('Increment Count2'),
6.Workers完整示例
以下是一个完整示例,展示了如何在 GetX 中使用 Workers。
1import 'package:flutter/material.dart';import 'package:get/get.dart';class MyController extends GetxController {print("Count1 changed: $count1"); everAll([count1, count2], (_) {print("Count1 or Count2 changed: count1 = $count1, count2 = $count2");print("Count1 changed for the first time: $count1");print("Count1 changed (with interval): $count1"); }, time: Duration(seconds: 1));print("Count1 changed (debounced): $count1"); }, time: Duration(seconds: 1));class MyApp extends StatelessWidget { Widget build(BuildContext context) {class HomeScreen extends StatelessWidget {final MyController controller = Get.put(MyController()); Widget build(BuildContext context) { title: Text('GetX Workers Example'), mainAxisAlignment: MainAxisAlignment.center, Obx(() => Text('Count1: ${controller.count1}')), Obx(() => Text('Count2: ${controller.count2}')), onPressed: controller.incrementCount1, child: Text('Increment Count1'), onPressed: controller.incrementCount2, child: Text('Increment Count2'),
5.唯一的ID
如果你想用GetBuilder完善一个widget的更新控件,你可以给它们分配唯一的ID。
GetBuilder
(
id: ‘text’, //这里
init: Controller(), // 每个控制器只用一次
builder: (_) => Text(
’${Get.find().counter}’, //here
),
),
并更新它:
update([‘text’]);
GetX会自动进行重建,并且只重建使用被更改的变量的小组件,如果您将一个变量更改为与之前相同的变量,并且不意味着状态的更改,GetX不会重建小组件以节省内存和CPU周期(界面上正在显示3,而您再次将变量更改为3。在大多数状态管理器中,这将导致一个新的重建,但在GetX中,如果事实上他的状态已经改变,那么widget将只被再次重建)