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