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),
      ),
    );
  }
}

效果图如下所示:

Flutter_Get_A.png


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'),
            ),
          ],
        ),
      ),
    );
  }
}

效果图如下所示:

Flutter_Get_B.png


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才会更新。效果图如下所示:

Flutter_Get_C.png


五、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'),
            ),
          ],
        ),
      ),
    );
  }
}

效果图如下所示:
Flutter_Get_D.png


六、监听自定义类数据的变化

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),
      ),
    );
  }
}

效果图如下所示:

Flutter_Get_E.png


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)


posted @   fengMisaka  阅读(237)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示