您的当前位置:首页正文

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

2024-11-11 来源:个人技术集锦


1. 引言

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

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

        Get并不是比任何其他状态管理器更好或更差,而是说你应该分析这些要点以及下面的要点来选择是只用Get,还是与其他状态管理器结合使用。Get不是其他状态管理器的敌人,因为Get是一个微框架,而不仅仅是一个状态管理器,它的状态管理功能既可以单独使用,也可以与其他状态管理器结合使用。

 2.安装和配置 GetX

        要在 Flutter 项目中使用 GetX,需要在 pubspec.yaml 文件中添加 GetX 依赖:。

        然后,在需要使用 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将只被再次重建)

Top