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);
  }
}
相关推荐
正小安1 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch3 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光3 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   3 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   3 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web3 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常3 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇4 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr4 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho5 小时前
【TypeScript】知识点梳理(三)
前端·typescript