Getx - 使用状态管理特性

添加到您的 pubspec.yaml 文件:

  1. dependencies:
  2. get:

1. 状态管理

状态管理其实就是管理应用的数据,在 get中也称为控制器,所有的数据都必须放在 继承自 GetController 的类中,才可以在视图中使用

第一步:创建 一个Controller

  1. import 'package:get/get.dart';
  2. /// 定义数据控制器
  3. class HomeController extends GetxController {}

第二步:在控制器中声明变量

  1. import 'package:get/get.dart';
  2. /// 定义数据控制器
  3. class HomeController extends GetxController {
  4. // 使用 .obs 作为你值的后缀
  5. final name = "老汪".obs;
  6. // 使用 Rx 和 Darts 泛型,Rx < type >
  7. final count = Rx<int>(0);
  8. // 使用 Rx { Type }
  9. final isLogged = RxBool(false);
  10. }

第三步:在UI中使用变量

  1. import 'package:flutter/material.dart';
  2. import 'package:getx_demo/page/home_controller.dart';
  3. class HomePage extends StatelessWidget {
  4. // 实例化控制器
  5. final controller = HomeController();
  6. @override
  7. Widget build(BuildContext context) {
  8. return Scaffold(
  9. appBar: AppBar(
  10. title: Text("状态管理"),
  11. centerTitle: true,
  12. ),
  13. body: Container(
  14. width: double.infinity,
  15. child: Column(
  16. crossAxisAlignment: CrossAxisAlignment.center,
  17. children: [
  18. Text('name: ${controller.name}'),
  19. Text('count: ${controller.count}'),
  20. Text('isLogged: ${controller.isLogged}'),
  21. Divider(),
  22. Text('循环items列表:'),
  23. Row(
  24. mainAxisAlignment: MainAxisAlignment.center,
  25. children:
  26. controller.items.map((item) => Text("$item, ")).toList(),
  27. )
  28. ],
  29. ),
  30. ),
  31. );
  32. }
  33. }

2. 声明“可观察的”变量

2.1 第一个是使用 Rx { Type }。

  1. final name = RxString('');
  2. final isLogged = RxBool(false);
  3. final count = RxInt(0);
  4. final balance = RxDouble(0.0);
  5. final items = RxList<String>([]);
  6. final myMap = RxMap<String, int>({});

2.2 第二种是使用 Rx 和 Darts 泛型,Rx < type >

  1. final name = Rx<string>('');
  2. final isLogged = Rx<bool>(false);
  3. final count = Rx<int>(0);
  4. final balance = Rx<double>(0.0);
  5. final number = Rx<num>(0)
  6. final items = Rx<List<string>>([]);
  7. final myMap = Rx<Map<string, int>>({});
  8. // 自定义类
  9. final user = Rx<User>();

2.3 第三种方法更实用、更简单也是首选,就是把.obs 作为你的变量的后缀:

  1. final name = ''.obs;
  2. final isLogged = false.obs;
  3. final count = 0.obs;
  4. final balance = 0.0.obs;
  5. final number = 0.obs;
  6. final items = <String>[].obs;
  7. final myMap = <String, int>{}.obs;
  8. // Custom classes - it can be any class, literally
  9. final user = User().obs;

3. 什么类型可被定义为的obs

你可以在 obs 上转换任何东西:

  1. import 'package:get/get.dart';
  2. class User {
  3. final name;
  4. final age;
  5. User({this.name, this.age});
  6. }
  7. class Teacher {
  8. final name = "李思思".obs;
  9. final languer = "语文".obs;
  10. }
  11. /// 定义数据控制器
  12. class HomeController extends GetxController {
  13. // 使用 .obs 作为你值的后缀
  14. final name = "老汪".obs;
  15. // 使用 Rx 和 Darts 泛型,Rx < type >
  16. final count = Rx<int>(0);
  17. // 使用 Rx { Type }
  18. final isLogged = RxBool(false);
  19. // 使用 .obs 作为你值的后缀
  20. final items = <String>["flutter", "getx", "redux", "java"].obs;
  21. // 自定义User类
  22. final user = User(name: "Camila", age: 18).obs;
  23. // 自定义teacher类
  24. final teacher = Teacher();
  25. // Map, 不响应的数据
  26. final subjects = {"name": "语文", "desc": "超扫good"};
  27. }

4. 如何改变数据的值

第一步: 计数器案例

  1. // 创建控制器
  2. class HomeController extends GetxController {
  3. // 使用 Rx 和 Darts 泛型,Rx < type >
  4. final count = Rx<int>(0);
  5. // 每次点击增加1
  6. void increment() {
  7. print("++")
  8. // 必须使用 .value 修饰具体的值
  9. this.count.value++;
  10. }
  11. }

第二步: UI中使用,并添加点击事件

  1. class HomePage extends StatelessWidget {
  2. // 实例化控制器
  3. final controller = HomeController();
  4. @override
  5. Widget build(BuildContext context) {
  6. return Scaffold(
  7. appBar: AppBar(
  8. title: Text("状态管理"),
  9. centerTitle: true,
  10. ),
  11. body: Container(
  12. width: double.infinity,
  13. child: Column(
  14. crossAxisAlignment: CrossAxisAlignment.center,
  15. children: [
  16. Text('数字类型: ${controller.count}'),
  17. MaterialButton(
  18. onPressed: () {
  19. // 直接调用函数
  20. controller.increment();
  21. },
  22. color: Colors.blue,
  23. child: Text("change数字类型"),
  24. )
  25. ],
  26. ),
  27. ),
  28. );
  29. }
  30. }

此刻点击,按钮发送事件,控制台会打印数据,数据也确实改变了,但视图不会更新

5. 如何触发视图的更新

在Get中, 有两个不同的状态管理器: 简单的状态管理器(GetBuilder) 和 响应式的管理器(GetX/Obx),所以只有

使用这两个状态管理器修饰的值才会引起视图的更新

5.1 使用GetBuilder()更新

GetBuilder 的目标正是多状态控制。想象一下,你在一个购物车中添加了30种商品,你点击删除一种,同时列表被更新,价格被更新,购物车中的徽章被更新为一个更小的数字。这种类型的方法使得 GetBuilder 成为杀手,因为它将状态分组并同时更改它们,而不需要任何“计算逻辑”。创建 GetBuilder 时就考虑到了这种情况,因为对于状态的临时更改,可以使用 setState,而且不需要状态管理器

这样,如果您想要一个单独的控制器,您可以为它分配 id,或者使用 GetX。这取决于您,记住您拥有的小部件越多,GetX 的性能就越突出,而当状态发生多次更改时,GetBuilder 的性能应该更好。

优点:

只更新所需的小部件。

使用较少内存(接近0mb)的状态管理器

5.1.1 GetBuilder的使用

第一步: 创建控制器

  1. /// 定义数据控制器
  2. class HomeController extends GetxController {
  3. // 注意,这里定义普通的值就行,不用使用.obs修饰
  4. int count = 0;
  5. // 需要手动调用update(),触发更新
  6. void increment() {
  7. count++;
  8. update();
  9. }
  10. }

第二步:渲染视图并触发更新

  1. class HomePage extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. return Scaffold(
  5. appBar: AppBar(
  6. title: Text("状态管理"),
  7. centerTitle: true,
  8. ),
  9. body: Container(
  10. width: double.infinity,
  11. child: Column(
  12. crossAxisAlignment: CrossAxisAlignment.center,
  13. children: [
  14. // 使用GetBuilder, 需要手动调用update()
  15. GetBuilder<HomeController>(
  16. init: HomeController(),
  17. builder: (_) => Column(
  18. children: [
  19. Text(
  20. '数字类型: ${_.count}',
  21. style: TextStyle(fontSize: 35),
  22. ),
  23. MaterialButton(
  24. onPressed: _.increment,
  25. color: Colors.blue,
  26. child: Text("++"),
  27. )
  28. ],
  29. ),
  30. ),
  31. ],
  32. ),
  33. ),
  34. );
  35. }
  36. }

5.1.2 如果想用 GetBuilder 具体更新某个具体的小部件,你可以给它们分配唯一的 id:

第一步:添加id属性

  1. GetBuilder<HomeController>(
  2. id: "num",
  3. init: HomeController(),
  4. builder: (_) => Column(
  5. children: [
  6. Text(
  7. '数字类型: ${_.count}',
  8. style: TextStyle(fontSize: 35),
  9. ),
  10. MaterialButton(
  11. onPressed: _.increment,
  12. color: Colors.blue,
  13. child: Text("++"),
  14. )
  15. ],
  16. ),
  17. )

第二步:update 时传入某个id属性

  1. // 直接按id更新
  2. void increment() {
  3. count++;
  4. update(["num"]);
  5. }
  6. // 您还可以为更新添加条件, 当count 小于 5 时才更新
  7. void increment() {
  8. count++;
  9. update(["num"], count < 5);
  10. }

5.2 使用Obx()更新

第一步: 创建控制器

  1. /// 定义数据控制器
  2. class HomeController extends GetxController {
  3. // 变量必须用final修饰
  4. final count = 0.obs;
  5. void increment() {
  6. count.value++;
  7. }
  8. }

第二步:渲染UI

  1. class HomePage extends StatelessWidget {
  2. // 实例化控制器
  3. final controller = HomeController();
  4. @override
  5. Widget build(BuildContext context) {
  6. return Scaffold(
  7. appBar: AppBar(
  8. title: Text("状态管理"),
  9. centerTitle: true,
  10. ),
  11. body: Container(
  12. width: double.infinity,
  13. child: Column(
  14. crossAxisAlignment: CrossAxisAlignment.center,
  15. children: [
  16. // 使用Obx返回渲染的 UI widget
  17. Obx(
  18. () => Text(
  19. '数字: ${controller.count}',
  20. style: TextStyle(fontSize: 35),
  21. ),
  22. ),
  23. // 添加点击事件
  24. MaterialButton(
  25. onPressed: controller.increment,
  26. color: Colors.blue,
  27. child: Text("++"),
  28. )
  29. ],
  30. ),
  31. ),
  32. );
  33. }
  34. }

在用户界面中,当你想要显示这个值并在值改变时更新屏幕时,只需要这样做:

注1:只有在 定义的变量的值发生变化时才会更改。

如果在一个 Obx 中有5多个 Rx (可观察的)变量,当它们中的任何一个发生变化时,它都会自动更新。

但如果我在一个类中有30个变量,当我更新一个时,它会更新该类中的所有变量吗?

并不会的,只有使用 Rx 变量的特定 Widget 才会更新,因此,当 Rx 变量改变它的值时,GetX 只更新屏幕


5.3 使用 GetX () 更新

用法同GetBuilder(), 但不能添加需要惟一的 id属性,

  1. class HomePage extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. return Scaffold(
  5. appBar: AppBar(
  6. title: Text("状态管理"),
  7. centerTitle: true,
  8. ),
  9. body: Container(
  10. width: double.infinity,
  11. child: Column(
  12. crossAxisAlignment: CrossAxisAlignment.center,
  13. children: [
  14. GetX<HomeController>(
  15. init: HomeController(),
  16. builder: (_) => Column(
  17. children: [
  18. Text(
  19. '数字类型: ${_.count}',
  20. style: TextStyle(fontSize: 35),
  21. ),
  22. MaterialButton(
  23. onPressed: _.increment,
  24. color: Colors.blue,
  25. child: Text("++"),
  26. )
  27. ],
  28. ),
  29. ),
  30. ],
  31. ),
  32. ),
  33. );
  34. }
  35. }

6. GetxController生命周期钩子

  • onInit : 初次渲染完毕,可以在这里定义控制器(输入框,顶部选项卡,PageView等),调接口
  • onClose: 组件卸载时触发,清除定时器等
  1. class MyController extends GetxController {
  2. @override
  3. onInit(){
  4. }
  5. @override
  6. void onClose() {
  7. }
  8. }

6. 1 演示输入框配合控制器,

  • 调接口就不演示了,放到 写 GetConnect 这个文章里
  1. // 控制器
  2. class HomeController extends GetxController {
  3. // 用户名控制器
  4. TextEditingController userNameController;
  5. @override
  6. void onInit() {
  7. super.onInit();
  8. userNameController = TextEditingController();
  9. print("渲染完成");
  10. }
  11. @override
  12. void onClose() {
  13. super.onClose();
  14. print("close");
  15. }
  16. }
  17. // UI 渲染
  18. class HomePage extends StatelessWidget {
  19. // 实例化控制器
  20. final controller = HomeController();
  21. @override
  22. Widget build(BuildContext context) {
  23. return Scaffold(
  24. appBar: AppBar(
  25. title: Text("状态管理"),
  26. centerTitle: true,
  27. ),
  28. body: Container(
  29. width: double.infinity,
  30. child: Column(
  31. crossAxisAlignment: CrossAxisAlignment.center,
  32. children: [
  33. TextField(
  34. controller: controller.userNameController,
  35. decoration: InputDecoration(
  36. hintText: "请输入用户名",
  37. ),
  38. onChanged: (val) {
  39. print(val);
  40. },
  41. )
  42. ],
  43. ),
  44. ),
  45. );
  46. }
  47. }

7. Get.find()

当有多个控制器时,指定使用某个具体的控制器

  1. // 现有 - HomeController, LoginController
  2. Get.find<LoginController>()

8. 混入状态 StateMixin (保存动态接口数据)

这个一般配合接口数据,在写 getConnect 一块时 说,可以简单看看

另一种处理 UI 状态的方法是使用 StateMixin < t > 。要实现它,可以使用 with 将 StateMixin < t > 添加到允许 t 模型的控制器中

  1. class Controller extends GetController with StateMixin<User>{}

方法可以随时改变状态,只需以下方式传递数据和状态:

  1. change(data, status: RxStatus.success());

允许这些状态:

  1. RxStatus.loading();
  2. RxStatus.success();
  3. RxStatus.empty();
  4. RxStatus.error('message');

要在 UI 中表示它,请使用:

  1. class OtherClass extends GetView<Controller> {
  2. @override
  3. Widget build(BuildContext context) {
  4. return Scaffold(
  5. body: controller.obx(
  6. (state)=>Text(state.name),
  7. onLoading: CustomLoadingIndicator(),
  8. onEmpty: Text('No data found'),
  9. onError: (error)=>Text(error),
  10. ),
  11. );
  12. }

9. 内置函数工具类 Woker(一般事件发生时触发特定的回调。)

  1. /// 变量发出新值时,都会调用
  2. ever(count1, (_) => print("$_ has been changed"));
  3. /// 在变量第一次被更改时才调用
  4. once(count1, (_) => print("$_ was changed once"));
  5. /// 防抖,连续点击多次,只执行一次,比如登录,搜索函数中非常有用
  6. debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
  7. /// 节流,如果用户在1秒内对一个变量进行了1000次更改,那么他只会在规定的计时器之后发送最后一次更改,默认为800毫秒
  8. interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));

10. 对比 GetBuilder vs GetX vs Obx

GetBuilder 在 内存消耗 方面非常经济,而且几乎没有比他更经济的方法了,但是,GetBuilder 仍然是一个机械状态管理器,您需要手动调用 update () 才会更新视图

GetX 仍然比其他任何反应状态管理器更经济,但是它比 GetBuilder 消耗更多的内存

Obx 比 GetX 更经济,但是输给了 GetBuilder, 与 GetX 和 GetBuilder 不同,您不能在 Obx 中初始化控制器

但使用 Obx,您不需要编写控制器类型,可以从多个不同的控制器中听到更改,但是需要在此之前进行初始化

一劳永逸,使用Obx, 然后是GetX, 追求极致性能使用 GetBuilder