Flutter对象状态动态监听Watcher

场景:当一个表单需要在表单全部或者特定项赋值后才会让提交按钮可点击。

1.普通实现方式:

Dart 复制代码
    ///场景:检查[test11][test12][test13]均不为空时做一些事情,例如提交按钮变成可点击
    String? test11;
    String? test12;
    int? test13;
    ///当需要检查[test11][test12][test13]是否全部有值时需要这么做。
    ///1.给[test11][test12][test13]设置监听,或者在他们赋值的时候每次调用2检查。
    ///2.检查三个值的状态
    if(test11!=null&&test12!=null&&test12!=null){
      ///print [test11][test12][test13]均不为空。
    }

2.使用Watcher方式实现:

Dart 复制代码
    ///使用[WatchableObject]配合[Watcher]监测
    ///1.只需要把String、int、bool、等对象用[WatchableObject]代替。
    WatchableObject test1 = WatchableObject();
    WatchableObject test2 = WatchableObject();
    WatchableObject test3 = WatchableObject();
    ///2.使用[Watcher]单例绑定对象。
    Watcher().bindObject([test1, test2, test3]);
    ///3.检查回调
    Watcher().check((allCheck) {
      print("?????????????????????=$allCheck");
    });
    ///4.模拟不同时间赋值
    Future.delayed(Duration(seconds: 3), () {
      test1.setValue("1");
    });
    Future.delayed(Duration(seconds: 6), () {
      test2.setValue("123");
    });
    Future.delayed(Duration(seconds: 8), () {
      test3.setValue(123);
    });



   ///退出界面时清除Watcher使用得内存
   @override
   void dispose() {
     super.dispose();
     Watcher().clear();
   }

logcat输出:

方式1和方式2都能达到效果,但是方式1需要对每一个变量进行监听,在每一个赋值的地方得检查所有得值是否都已有了值,这样实现的出错率就会变得很高。方式2则是利用变量托管,托管类已实现了对变量的赋值的监听,只要使用托管类WatchableObject包装变量,则可以实时监听到变量的赋值变化,所以代码上,对变量的使用不会再对变量进行任何监听和处理,统一会由Watcher类进行回调处理。方式1的缺点就是代码混乱,容易出错。方式2的优点可以解决方式1的缺点,但是缺点是使用到Watcher的地方,变量必须交给WatchableObject托管,导致定义变量的时候变得麻烦,但是这个只要使用习惯了,确可以忽略该缺点。

喜欢这种方式的或者有需求用得到的朋友来撸代码吧:

WatchableObject类:

Dart 复制代码
import 'dart:math';

import 'package:kq_flutter_widgets/widgets/listener/object_watcher/watcher.dart';
import 'package:kq_flutter_widgets/widgets/listener/object_watcher/watcher_callback.dart';

///可观察对象,
///[T]可观察的对象类型。
///例如:传入的是String,则会持有String对象,
///并可以设置[watcher]观察绑定的String对象值的变化。
class WatchableObject<T> {
  T? _watchableObject;
  double? _uuid;

  ///初始值
  WatchableObject({T? init}) {
    setValue(init);
  }

  ///设置值
  void setValue(T? other) {
    _watchableObject = Watcher().value(getUuid(), other);
  }

  ///设置观察者
  void watcher(WatcherCallback watcherCallback) {
    Watcher().watcher(getUuid(), watcherCallback);
  }

  ///获取值
  T? value() {
    return _watchableObject;
  }

  ///获取uuid
  double getUuid() {
    return _uuid ??= Random().nextDouble();
  }
}

WatcherCallback类:

Dart 复制代码
///回调
class WatcherCallback<T> {
  ///值改变回调函数,
  ///[object]改变的值。
  final Function(T? object) onChanged;

  WatcherCallback(this.onChanged);
}

Watcher类:

Dart 复制代码
import 'package:kq_flutter_widgets/widgets/listener/object_watcher/watchable_object.dart';
import 'package:kq_flutter_widgets/widgets/listener/object_watcher/watcher_callback.dart';

///对象观察者
class Watcher {
  Watcher._internal();

  factory Watcher() => _instance;
  static final Watcher _instance = Watcher._internal();

  final Map<double, WatcherCallback> _objectWatchers = {};
  final List<WatchableObject> _objects = [];
  final Map<double, bool> _bindObjects = {};

  ///绑定对象,
  ///[objects]绑定的一组对象。
  void bindObject(List<WatchableObject> objects) {
    clear();
    _objects.addAll(objects);
    for (WatchableObject str in objects) {
      _bindObjects.putIfAbsent(str.getUuid(), () => _checkObject(str.value()));
    }
  }

  ///检查绑定的对象是否已全部赋值,
  ///[callback]每次赋值都会回调,
  ///[allCheck]是否全部已赋值,是则返回true,不是则返回false。
  void check<T>(Function(bool allCheck) callback) {
    for (WatchableObject str in _objects) {
      str.watcher(
        WatcherCallback(
          (object) {
            _bindObjects.update(
              str.getUuid(),
              (value) => _checkObject(object),
            );
            _realCheck(callback);
          },
        ),
      );
    }
    _realCheck(callback);
  }

  bool _checkObject<T>(T object) {
    return object is String ? object.isNotEmpty : object != null;
  }

  void _realCheck(Function(bool allCheck) callback) {
    bool isAllCheck = true;
    _bindObjects.forEach((key, value) {
      if (!value) {
        isAllCheck = false;
        return;
      }
    });
    callback.call(isAllCheck);
  }

  ///清除内存。
  void clear() {
    _objects.clear();
    _bindObjects.clear();
    _objectWatchers.clear();
  }

  ///绑定回调执行,
  ///需要[WatchableObject]对象的[uuid]做为键值获取对象绑定。
  T? value<T>(double uuid, T? other) {
    _objectWatchers[uuid]?.onChanged(other);
    return other;
  }

  ///绑定设置,
  ///需要[WatchableObject]对象的[uuid]做为键值,
  ///[watcherCallback]绑定的回调。
  void watcher(double uuid, WatcherCallback watcherCallback) {
    _objectWatchers.putIfAbsent(uuid, () => watcherCallback);
  }
}
相关推荐
拉不动的猪6 分钟前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy1 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom1 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom1 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom1 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom2 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom2 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试
LaoZhangAI3 小时前
2025最全GPT-4o图像生成API指南:官方接口配置+15个实用提示词【保姆级教程】
前端
ONE_Gua3 小时前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·后端·爬虫
LaoZhangAI3 小时前
2025最全Cherry Studio使用MCP指南:8种强大工具配置方法与实战案例
前端