深入浅出 GetX:超详细实用指南,全方位掌握 Flutter 开发利器第二篇之状态管理篇

目录

1. 引言

 2.安装和配置 GetX

3.GetX 状态管理的核心概念

1.响应式编程

2.控制器 (Controller)

3.状态 (State)

4.状态更新(State Update)

 5. 基本用法示例

1.Obx

 2.GetX

3.GetBuilder

4.Workers

1.ever

2.everAll

3.once

4.interval

5.debounce

6.Workers完整示例

5.唯一的ID


1. 引言

        在 Flutter 开发中,状态管理是一个至关重要的概念。它决定了应用的性能和维护性。GetX 是一个强大的 Flutter 包,它提供了高效、简洁的状态管理、依赖管理和路由管理功能。本文将深入探讨 GetX 的状态管理,并展示如何在 Flutter 应用中使用它。

        Flutter也有其它的状态管理器,它们有着各自的特点:

  1. BLoC非常安全和高效,但是对于初学者来说非常复杂,这使得人们无法使用Flutter进行开发。
  2. MobX比BLoC更容易,而且是响应式的,几乎是完美的,但是你需要使用一个代码生成器,对于大型应用来说,这降低了生产力,因为你需要喝很多咖啡,直到你的代码在flutter clean之后再次准备好(这不是MobX的错,而是codegen真的很慢!)。
  3. 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:反应式,只在依赖的变量变化时更新。

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

 5. 基本用法示例

1.Obx

        以下图的效果为例。我们有两个变量,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()}'),),

          ],
        ),
      ),
    );
  }
}

 2.GetX

        还是上述图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中的方法之后,被观察的值立即改变。

3.GetBuilder

        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

4.Workers

        在 GetX 中,Workers 是一种监控和响应 Rx 类型变量变化的机制。它们可以在变量变化时执行特定的回调函数,帮助开发者简化逻辑处理和状态更新。Workers 包括以下几种类型:

        ever、everAll、once、interval、debounce

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++;
  }

}

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++;
  }
}

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++;
  }
}

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

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, (_) {
      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.唯一的ID

        如果你想用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将只被再次重建)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫柱子哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值