Flutter插件Get(2):状态管理篇.md
一、状态管理说明
状态
- 百度百科:状态是人或事物表现出来的形态。是指现实(或虚拟)事物处于生成、生存、发展、消亡时期或各转化临界点时的形态或事物态势
- 此处:指的是变量的值
状态管理的方式
- 其他状态管理器:Streams 或者 ChangeNotifier
- 此处:反应式状态管理(GetX/Obx)和简单状态管理(GetBuilder)
二、响应状态管理
2.1 使用方法
例如使用 GetX 改造 Flutter 默认的计数器示例应用,只需要三步:
(1)在定义变量时,在变量末尾加上.obs
后缀:
final _counter = 0.obs;
(2)在使用变量的地方,使用Obx
来包裹:
Obx(() => Text("${_counter}"))
(3)点击按钮的时候,直接修改变量值,UI 即可自动刷新:
onPressed: () {_counter.value++}
这就是全部,就这么简单。甚至连setState
都不需要调用。
2.2 计数器示例
改写后的全部代码如下:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
appBarTheme: const AppBarTheme(
centerTitle: true,
)),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({super.key});
final _counter = 0.obs; // (1)在定义变量时,在变量末尾加上.obs后缀
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Gex 计数器"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Obx(() => Text("${_counter.value}")) // (2)在使用变量的地方,使用Obx来包裹
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_counter.value++; // (3)点击按钮的时候,直接修改变量值,UI 即可自动刷新
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
效果图如下所示:
2.3 补充:声明一个响应式变量三种方式
第一种 使用 Rx{Type}。
final name = RxString('');
final isLogged = RxBool(false);
final count = RxInt(0);
final balance = RxDouble(0.0);
final items = RxList<String>([]);
final myMap = RxMap<String, int>({});
第二种是使用 Rx,规定泛型 Rx。
final name = Rx<String>('');
final isLogged = Rx<Bool>(false);
final count = Rx<Int>(0);
final balance = Rx<Double>(0.0);
final number = Rx<Num>(0)
final items = Rx<List<String>>([]);
final myMap = Rx<Map<String, int>>({});
自定义类 - 可以是任何类
final user = Rx<User>();
第三种 更实用、更简单、更可取的方法,只需添加.obs
作为value的属性。(推荐)
final name = ''.obs;
final isLogged = false.obs;
final count = 0.obs;
final balance = 0.0.obs;
final number = 0.obs;
final items = <String>[].obs;
final myMap = <String, int>{}.obs;
自定义类 - 可以是任何类
final user = User().obs;
三、GetxController(控制器)
3.1 使用方法
上面的计时器示例,我们还可以使用 GetxController 来实现。
顾名思义,controller 也就是控制器的意思,这个类的出现是为了减少代码的低耦合。我们把业务代码放到 GetxController 中,减少 Widget 部分的代码。
(1)我们定义一个继承自 GetxController 的控制器,代码如下:
// 控制器
class Controller extends GetxController {
final _counter = 0.obs;
// 增加 _counter 的值
void incrementCount() {
_counter.value++;
}
}
(2)使用 Get.put 以确保控制器实例在需要时被正确管理:
final Controller controller = Get.put(Controller());
(3)然后我们使用 GetX 包在 Widget 的外层,即可对变量的精准控制。
class MyHomePage extends StatelessWidget {
MyHomePage({super.key});
// (2)使用 Get.put 以确保控制器实例在需要时被正确管理
final Controller controller = Get.put(Controller());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Gex 计数器"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
// (3)使用 GetX 包在 Widget 的外层,即可对变量的精准控制
GetX<Controller>(
builder: (controller) {
debugPrint("_counter rebuild");
return Text('_counter: ${controller._counter.value}');
},
),
ElevatedButton(
onPressed: controller.incrementCount,
child: const Text('Increment Count2'),
),
],
),
),
);
}
}
上述的过程是自动的,也就是当我们调用 GexController 中的方法之后,被观察的值立即改变。
3.2 计数器示例
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
// (1)定义控制器
class Controller extends GetxController {
final _counter = 0.obs;
// 增加 _counter 的值
void incrementCount() {
_counter.value++;
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
appBarTheme: const AppBarTheme(
centerTitle: true,
)),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({super.key});
// (2)使用 Get.put 以确保控制器实例在需要时被正确管理
final Controller controller = Get.put(Controller());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Gex 计数器"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
// (3)使用 GetX 包在 Widget 的外层,即可对变量的精准控制
GetX<Controller>(
builder: (controller) {
debugPrint("_counter rebuild");
return Text('_counter: ${controller._counter.value}');
},
),
ElevatedButton(
onPressed: controller.incrementCount,
child: const Text('Increment Count'),
),
],
),
),
);
}
}
效果图如下所示:
3.3 GetxController的生命周期
GetX 中的 Controller 是有生命周期的,它提供了三个生命周期方法:
onInit()
: 在 widget 页面组件从内存创建时立即执行,可以用来执行初始化业务,例如页面初始数据请求。onReady()
: 在onInit()
之后调用1帧,它是插入导航事件的理想时机,如 snackbar、dialogs, 或者一个新的路由或异步请求等。onClose()
: 页面销毁时调用,可以用来清理资源,或做持久化数据保存。
class SimpleController extends GetxController {
@override
void onInit() {
// TODO: implement onInit
debugPrint("初始化");
super.onInit();
}
@override
void onReady() {
// TODO: implement onReady
debugPrint("加载完成");
super.onReady();
}
@override
void onClose() {
// TODO: implement onClose
debugPrint("控制器被释放");
super.onClose();
}
}
四、GetBuilder
4.1 使用方法
GetBuilder 的用法和 GetxController 基本相同。区别就是如果使用 GetBuilder 必须手动的调用 GetxController 的 update 方法页面才会刷新。
(1)我们定义一个继承自 GetxController 的控制器,代码如下:
// (1)定义控制器
class GetBuilderController extends GetxController {
final _counter = 0.obs;
// 增加 _counter 的值
void incrementCount() {
_counter.value++;
}
}
(2)使用 Get.put 以确保控制器实例在需要时被正确管理:
final GetBuilderController controller = Get.put(GetBuilderController());
(3)然后我们使用 GetX 包在 Widget 的外层,即可对变量的精准控制。
GetBuilder<GetBuilderController>(
builder: (controller) {
debugPrint("count rebuild");
return Text('count: ${controller._counter.value}');
},
),
(4)使用按钮调用 update 方法:
ElevatedButton(
onPressed: controller.update,
child: const Text('update'),
),
4.2 计数器示例
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
// (1)定义控制器
class GetBuilderController extends GetxController {
final _counter = 0.obs;
// 增加 _counter 的值
void incrementCount() {
_counter.value++;
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
appBarTheme: const AppBarTheme(
centerTitle: true,
)),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({super.key});
// (2)使用 Get.put 以确保控制器实例在需要时被正确管理
final GetBuilderController controller = Get.put(GetBuilderController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Gex 计数器"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
// (3)然后我们使用 GetX 包在 Widget 的外层,即可对变量的精准控制。
GetBuilder<GetBuilderController>(
builder: (controller) {
debugPrint("count rebuild");
return Text('count: ${controller._counter.value}');
},
),
ElevatedButton(
onPressed: controller.incrementCount,
child: const Text('Increment Count'),
),
// (4)使用按钮调用 update 方法
ElevatedButton(
onPressed: controller.update,
child: const Text('update'),
),
],
),
),
);
}
}
当我们点击按钮之后,调用incrementCount方法,count的值并不会马上更新,只有手动的调用update方法,count才会更新。效果图如下所示:
五、Workers
在 GetX 中,Workers 是一种监控和响应 Rx 类型变量变化的机制。它们可以在变量变化时执行特定的回调函数,帮助开发者简化逻辑处理和状态更新。Workers 包括以下几种类型:
- ever
- everAll
- once
- interval
- debounce
5.1 ever
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++;
}
}
5.2 everAll
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++;
}
}
5.3 once
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++;
}
}
5.4 interval
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++;
}
}
5.5 debounce
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'),
),
],
),
),
);
}
5.6 Workers完整示例
以下是一个完整示例,展示了如何在 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, (_) {
debugPrint("Count1 changed: $count1");
});
// 任意一个 count1 或 count2 变化时都会执行回调
everAll([count1, count2], (_) {
debugPrint(
"Count1 or Count2 changed: count1 = $count1, count2 = $count2");
});
// count1 第一次变化时执行回调
once(count1, (_) {
debugPrint("Count1 changed for the first time: $count1");
});
// count1 变化时每隔1秒执行一次回调
interval(count1, (_) {
debugPrint("Count1 changed (with interval): $count1");
}, time: const Duration(seconds: 1));
// count1 变化后1秒内没有再次变化时执行回调
debounce(count1, (_) {
debugPrint("Count1 changed (debounced): $count1");
}, time: const Duration(seconds: 1));
}
void incrementCount1() {
count1++;
}
void incrementCount2() {
count2++;
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
HomeScreen({super.key});
final MyController controller = Get.put(MyController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const 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: const Text('Increment Count1'),
),
ElevatedButton(
onPressed: controller.incrementCount2,
child: const Text('Increment Count2'),
),
],
),
),
);
}
}
效果图如下所示:
六、监听自定义类数据的变化
6.1 类里面的属性响应式变化
创建 Person 类,使用响应式方式修饰成员变量:
class Person {
RxString name = "Jimi".obs; // rx 变量
RxInt age = 18.obs;
}
然后可以获取类属性值以及改变值。完整示例如下:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
class Person {
RxString name = "Jimi".obs; // rx 变量
RxInt age = 18.obs;
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var person = Person(); // 定义 Person 对象
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Getx Obx"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Obx(() => Text(
// 读取时使用Obx包裹
"我的名字是 ${person.name}",
style: const TextStyle(color: Colors.red, fontSize: 30),
))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
person.name.value = person.name.value.toUpperCase(); // 修改值
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
效果图如下所示:
6.2 整个类的响应式变化
另一种方式,还可以定义整个类为响应式,例如:
import 'package:get/get.dart';
class Animal {
String username;
int age;
Animal(this.username,this.age);
}
在创建上面的 Animal
对象时,可以直接后面加.obs
后缀:
var a = Animal("xiao mao", 2).obs;
使用的时候还是用Obx
包装:
Obx(() => Text(a.username))
修改的时候,需要修改变量指针的指向:
onPressed: () => a.value = Animal("小狗", 3)
参考:
Flutter开发之——getX-状态管理(02)_getx getbuilder-CSDN博客
Flutter中GetX的用法(超详细使用指南之状态管理篇)_flutter getx 状态管理-CSDN博客
Flutter Getx 状态管理 --- 响应式状态管理器 - 鲤斌 - 博客园 (cnblogs.com)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库