GetX:响应式监听工具Worker

  1. 是什么
  2. Worker 的类型
    1. ever
    2. once
    3. debounce
    4. interval
    5. everAll
    6. 防抖与节流
  3. 案例

是什么

本质上是一个 订阅器(subscription),它会订阅某个 Rx 变量。

每个 Worker 都返回一个 Worker 实例,你可以通过 worker.dispose() 来手动取消监听。

作用:是用来监听一个 Rx 变量,然后在满足不同的触发条件时执行回调

用途:把副作用逻辑(比如日志打印、网络请求、弹窗提示等)从控制器的 build 方法里分离出来。

Worker 的生命周期

  • 一般写在 GetxController.onInit() 中(你写的那样)。
  • 因为控制器的生命周期结束时,GetX 会自动 dispose 这些 Worker,不需要你手动清理。
  • 如果是自己写在 UI 里,就要注意保存 Worker 对象,手动 dispose(),否则可能内存泄漏。

基本语法:

Worker ever<RxType>(
  RxInterface<RxType> listener,       // 要监听的 Rx 变量
  WorkerCallback<RxType> callback,    // 触发时执行的回调
)
// callback 会有一个参数:这个参数是 Rx的值,也就是listener的值
  (value)={...}

Worker 的类型

ever

ever(_count, (value) {
  print("每次变化都执行 -> $value");
});

作用:每次 _count 发生变化时都会触发回调。

特点:只要有变化,就立刻执行。

使用场景:想要对状态的每次变化都做处理,比如写日志、做 UI 更新、同步远端数据等。

once

once(_count, (value) {
  debugPrint("once -> $value");
});
  • 作用:只会触发一次,当 _count 第一次发生变化时执行,之后就不会再执行了。
  • 使用场景:某个事件只需要监听并处理一次,比如用户第一次登录成功后要做初始化工作。

debounce

debounce(_count, (value) {
  debugPrint("debounce -> $value");
}, time: Duration(seconds: 2));
  • 作用:防抖(debounce)。
    • _count 变化时不会立即执行,而是等到 2 秒内不再有变化时才执行
  • 使用场景:
    • 用户在输入搜索框内容时,频繁触发变化,但只想在用户停止输入一段时间后再执行搜索。
    • 减少高频触发带来的资源浪费。

interval

interval(_count, (value) {
  debugPrint("interval -> $value");
}, time: Duration(seconds: 1));

作用:节流(throttle)。

  • _count 变化时,最多每 1 秒触发一次回调。

使用场景:

  • 处理一些短时间内可能被疯狂触发的事件,比如按钮点击、滚动事件等。
  • 可以避免回调函数执行过于频繁,降低性能消耗。

everAll

everAll([_count, _count2], (values) {
  print("任意一个变化都执行 -> $values");
});

everAll 的第一个参数是一个 Rx 变量的列表,比如 [_count, _count2]

每当 其中任意一个 Rx 变量发生变化,都会触发回调。

回调参数 values 是一个 List,里面包含了你传入的所有 Rx 的当前值,顺序和你传入的列表一致

防抖与节流

“防抖(debounce)”“节流(throttle / interval)” 是前端、移动端开发里常见的两个概念,它们都是用来 控制高频事件的触发次数,避免因为用户操作太频繁而带来性能浪费。

防抖(Debounce):事件被触发后,会等一段时间;如果这段时间内事件又被触发,就重新计时。只有等用户停止操作,超过设定时间后才执行一次。

  • 一句话“等你不动了再执行”
  • 例子
    • 你在搜索框里输入「flutter」
      • 输入 f → 等待中
      • 输入 fl → 等待中
      • 输入 flu → 等待中
      • …(只要一直在输入就不触发)
      • 停止输入 2 秒 → 只触发一次 搜索。
  • 应用场景
    • 搜索框输入:用户边打字边触发输入事件,但只想在用户停止输入 500ms 后再发送请求。
    • 表单校验。

节流(throttle / interval):无论事件触发多频繁,都按照一定的时间间隔执行一次。

  • 一句话“定时触发,不管你点多少次”
  • 例子
    • 你疯狂点击按钮 10 次(间隔 < 1 秒)
    • interval(1秒) → 1 秒内只会响应 1 次
    • 如果你连续点击 5 秒,就会触发大约 5 次。
  • 应用场景
    • 按钮点击:避免用户在 1 秒内疯狂点击按钮导致重复提交。
    • 页面滚动:监听滚动事件,但只想每隔 200ms 处理一次,避免卡顿。

案例

class StateWorkersView extends StatelessWidget {
  StateWorkersView({super.key});
  // 使用 Get.put() 注册控制器
  // 使用直接手动创建的方式已经不行了,不会去执行 ever、once、debounce、interval 等方法
  // final controller = CountController();
  final CountController controller = Get.put(CountController());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("GetBuilder")),
      body: Center(
        child: Column(
          children: [
            // 显示
            GetX<CountController>(
              init: controller,
              initState: (_) {},
              builder: (ctrl) {
                return Text('value -> ${ctrl.count}');
              },
            ),

            // 按钮
            ElevatedButton(
              onPressed: () {
                controller.add();
              },
              child: Text('add'),
            ),
          ],
        ),
      ),
    );
  }
}

class CountController extends GetxController {
  final _count = 0.obs;
  set count(value) => _count.value = value;
  get count => _count.value;
  final _count2 = 20.obs;
  set count2(value) => _count2.value = value;
  get count2 => _count2.value;

  add() {
    _count.value++;
    _count2.value++;
  }

  @override
  void onInit() {
    super.onInit();
    debugPrint("------------onInit----------");

    // 每次
    ever(_count, (value) {
      debugPrint("ever -> $value");
    });

    // 第一次
    once(_count, (value) {
      debugPrint("once -> $value");
    });

    // 防抖 2 秒内
    debounce(_count, (value) {
      debugPrint("debounce -> $value");
    }, time: Duration(seconds: 2));

    // 定时器 1 秒
    interval(_count, (value) {
      debugPrint("interval -> $value");
    }, time: Duration(seconds: 1));
    // 监听多个值
    everAll([_count, _count2], (values) {
      debugPrint("任意一个变化都执行 -> ${values.toString()}");
    });
  }
}

下面是打印的日志:

Restarted application in 1,618ms.
[GETX] Instance "CountController" has been created
I/flutter ( 3720): ------------onInit----------
[GETX] Instance "CountController" has been initialized
[GETX] Instance "GetMaterialController" has been created
[GETX] Instance "GetMaterialController" has been initialized
I/flutter ( 3720): ever -> 1
[GETX] Worker [once] called
I/flutter ( 3720): once -> 1
I/flutter ( 3720): 任意一个变化都执行 -> 1
I/flutter ( 3720): 任意一个变化都执行 -> 21
I/flutter ( 3720): interval -> 1
I/flutter ( 3720): debounce -> 1
I/flutter ( 3720): ever -> 2
I/flutter ( 3720): 任意一个变化都执行 -> 2
I/flutter ( 3720): 任意一个变化都执行 -> 22
I/flutter ( 3720): interval -> 2
I/flutter ( 3720): debounce -> 2

×

喜欢就点赞,疼爱就打赏