关于flutter:源码篇Flutter-GetX深度剖析-我们终将走出自己的路万字图文

43次阅读

共计 36040 个字符,预计需要花费 91 分钟才能阅读完成。

前言

人心中的偏见是一座大山,任你怎么致力都休想搬动。

这是电影《哪吒》里申公豹说的一句话,也是贯彻整部电影的一个主题;或者这句话引起了太多人的共鸣:35 岁职场危机,大厂卡本科学历,无房无车结婚难等等,所以,这句话也常常被人提起。

同时,因为 GetX 作者的一些舆论,也让一些偏见始终随同着 GetX 这个框架。

我写这篇文章,并不是为 GetX 正名

  • 我自问本人并不是任何一个状态框架的死忠者,Provider 和 Bloc,我写了相干应用、原理分析文章和相干代码生成插件
  • 在我心中,这类框架并没有如许神秘
  • 因为对其原理较熟,上手应用是一件较为容易的事,所以切换相干框架没有多大的工夫老本
  • 所以,我无需去做一个卫道者

GetX 整体设计,有不少优良点思维,我心愿将这些优良设计思路展示给大家;或者会对你设计本人的框架有一些帮忙,同时也是对本人思考历程的一个记录。

前置常识

在说 GetX 设计思维之前,须要先介绍几个常识,在 Flutter 茁壮倒退的历程里,他们都留下了浓墨重彩的一笔

InheritedWidget

不得不说,这个控件真的是一个神奇控件,它就好像是一把神兵利器

  • 宝刀屠龙,号令天下,莫敢不从,倚天不出,谁与争锋
  • 倚天剑,剑藏《九阴真经》
  • 屠龙刀,刀藏《降龙十八掌》、《武穆遗书》

InheritedWidget 这把神兵藏有什么?

  • 依赖节点,数据传输
  • 定点刷新机制

数据传输

InheritedWidget 是咱们统称的一个控件名,精华还是InheritedElement,InheritedWidget 的数据传递,来看下存和取这俩个过程

存数据

  • InheritedWidget 存数据,是一个比较简单的操作,存储在 InheritedElement 中即可
class TransferDataWidget extends InheritedWidget {TransferDataWidget({required Widget child}) : super(child: child);

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => false;

  @override
  InheritedElement createElement() => TransferDataElement(this);
}

class TransferDataElement extends InheritedElement {TransferDataElement(InheritedWidget widget) : super(widget);

  /// 轻易初始化什么, 设置只读都行
  String value = '传输数据';
}

取数据

  • 只有是 TransferDataWidget(下面 InheritedWidget 的子类) 的子节点,通过子节点的 BuildContext(Element 是 BuildContext 的实现类),都能够无缝的取数据
var transferDataElement = context.getElementForInheritedWidgetOfExactType<TransferDataWidget>()
            as TransferDataElement?;
var msg = transferDataElement.value;

能够发现,咱们只须要通过 Element 的 getElementForInheritedWidgetOfExactType 办法,就能够拿到父节点的 TransferDataElement 实例(必须继承 InheritedElement)

拿到实例后,天然就能够很简略的拿到相应数据了

原理

  • 能够发现咱们是拿到了 XxxInheritedElement 实例,继而拿到了贮存的值,所以要害在 getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() 这个办法

    • 代码很简略,就是从 _inheritedWidgets 这个 map 里取值,泛型 T 是 key
abstract class Element extends DiagnosticableTree implements BuildContext {

    Map<Type, InheritedElement>? _inheritedWidgets;

    @override
    InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {assert(_debugCheckStateIsActiveForAncestorLookup());
        final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
        return ancestor;
    }
    
    ...
}
  • 接下来只有搞清楚 _inheritedWidgets 是怎么存值,那么所有都会清朗
abstract class ComponentElement extends Element {
    
  @mustCallSuper
  void mount(Element? parent, dynamic newSlot) {
    ...
    _updateInheritance();}
    
  void _updateInheritance() {assert(_lifecycleState == _ElementLifecycle.active);
    _inheritedWidgets = _parent?._inheritedWidgets;
  }
    
    ...
}

abstract class ProxyElement extends ComponentElement {...}

class InheritedElement extends ProxyElement {InheritedElement(InheritedWidget widget) : super(widget);

    @override
    void _updateInheritance() {assert(_lifecycleState == _ElementLifecycle.active);
        final Map<Type, InheritedElement>? incomingWidgets = _parent?._inheritedWidgets;
        if (incomingWidgets != null)
            _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
        else
            _inheritedWidgets = HashMap<Type, InheritedElement>();
        _inheritedWidgets![widget.runtimeType] = this;
    }
}

整体上逻辑还是比拟清晰

  1. 遇到某个节点为 InheritedWidget 时,会将父节点的 _inheritedWidgets 变量给 incomingWidgets 这个长期变量

    1. incomingWidgets 为空:为父类 Element 的 _inheritedWidgets 变量,实例了一个 map 对象
    2. incomingWidgets 不为空:将父节点_inheritedWidgets 所有数据深拷贝,返回一个全新的 Map 实例(含有父节点 _inheritedWidgets 所有数据),赋值给父类 Element 的 _inheritedWidgets 变量
  2. 将本身实例赋值给父类 Element 的 _inheritedWidgets 变量,key 为其 widget 的 runtimeType

为什么任何一个 Widget 的 Element 实例的 _inheritedWidgets 变量,可间接拿到父节点 InheritedElement 实例?

  • Element 中做了一个父节点向子节点赋值的操作:整个数据传输链清晰了
abstract class Element extends DiagnosticableTree implements BuildContext {

    Map<Type, InheritedElement>? _inheritedWidgets;

    void _updateInheritance() {assert(_lifecycleState == _ElementLifecycle.active);
        _inheritedWidgets = _parent?._inheritedWidgets;
    }

    ...
}
  • 图示

刷新机制

InheritedElement 和 Element 之间有一些交互,实际上自带了一套刷新机制

  • InheritedElement 存子节点 Element:_dependents,这个变量是用来存储,须要刷新的子 Element
class InheritedElement extends ProxyElement {InheritedElement(InheritedWidget widget) : super(widget);

  final Map<Element, Object?> _dependents = HashMap<Element, Object?>();

  @protected
  void setDependencies(Element dependent, Object? value) {_dependents[dependent] = value;
  }

  @protected
  void updateDependencies(Element dependent, Object? aspect) {setDependencies(dependent, null);
  }
}
  • InheritedElement 刷新子 Element

    • notifyClients 这个办法就是将 _dependents 存储的 Element,全副拿了进去,传入 notifyDependent
    • 在 notifyDependent 办法中,传入 Element 调用了本身 didChangeDependencies()办法
    • Element 的 didChangeDependencies() 办法会调用 markNeedsBuild(),来刷新本身
class InheritedElement extends ProxyElement {InheritedElement(InheritedWidget widget) : super(widget);

  final Map<Element, Object?> _dependents = HashMap<Element, Object?>();

  @protected
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {dependent.didChangeDependencies();
  }
    
  @override
  void notifyClients(InheritedWidget oldWidget) {for (final Element dependent in _dependents.keys) {
      ...
      notifyDependent(oldWidget, dependent);
    }
  }
}

abstract class Element extends DiagnosticableTree implements BuildContext {
  ...
    
  @mustCallSuper
  void didChangeDependencies() {assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
    assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
    markNeedsBuild();}
    
  ...
}

InheritedWidget 的子节点是怎么将本身 Element

增加到 InheritedElement 的_dependents 变量里的呢?

  • Element 外面有个 dependOnInheritedElement 办法

    • Element 中 dependOnInheritedElement 办法,会传入 InheritedElement 实例 ancestor
    • ancestor 调用 updateDependencies 办法,将本身的 Element 实例传入
    • InheritedElement 中就将这个 Element,增加到_dependents 变量中了
abstract class Element extends DiagnosticableTree implements BuildContext {
  ...
    
  @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect}) {assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies!.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }
    
  ...
}

class InheritedElement extends ProxyElement {InheritedElement(InheritedWidget widget) : super(widget);

  final Map<Element, Object?> _dependents = HashMap<Element, Object?>();

  @protected
  void setDependencies(Element dependent, Object? value) {_dependents[dependent] = value;
  }

  @protected
  void updateDependencies(Element dependent, Object? aspect) {setDependencies(dependent, null);
  }
}
  • dependOnInheritedElement 该办法的应用也很简略

    • 一般来说在某个 Widget 应用 getElementForInheritedWidgetOfExactType 获取父节点的 InheritedElement
    • 而后将其传入 dependOnInheritedElement 办法中即可
// 举例
var inheritedElement = context
            .getElementForInheritedWidgetOfExactType<ChangeNotifierEasyP<T>>()
        as EasyPInheritedElement<T>?;
context.dependOnInheritedElement(inheritedElement);

Provider 外围原理,就是采纳了 InheritedWidget 这种刷新机制

想具体理解 Provider 相干原理,可参考上面文章

  • 源码篇:Flutter Provider 的另一面(万字图文)

图示

  • 来看下 InheritedWidget 刷新机制的图示

路由小常识

  • 路由 Navigator 中根本都是些操作路由的静态方法,NavigatorState 是实现的具体逻辑

大家在应用 InheritedWidget 获取数据的时候,或者有过这样一种困扰:A 页面 —> B 页面 —> C 页面

如果我在 A 页面应用 InheritedWidget 贮存了数据,跳转到 B 页面或者 C 页面,会发现应用 context 获取不到 A 页面的 InheritedElement

这侧面证实了 Navigator 路由跳转:A 页面跳转 B 页面,B 页面并不是 A 页面的子节点

  • 大抵构造:可勉强了解为,Navigator 是所有页面父节点,页面与页面之间是平级关系

这里我画了下大抵构造,如有偏差,请务必指出来,我会尽快批改

对于 Flutter 路由原理解析,可参考此文章(作者为啥当初不更文了呢 ~~):Flutter 路由原理解析

思考

InheritedWidget 为咱们带了很多便当

  • 能够在一个 Widget 树范畴,获取到咱们想要 InheritedElement(通过泛型辨别即可)
  • InheritedElement 和 Element 各种交互,也实现了一套极其简洁的刷新机制
  • 进行一些深度封装,甚至能够无缝的治理很多控件的资源开释

然而,Element 提供的获取 InheritedElement 的形式,究竟和路由机制无奈很好的联合;这也模块设计无奈防止的事件,或者某些模块设计的最优解,很难顾虑到其它模快的一些机制

InheritedWidget 这把神兵利器,在咱们学习 Flutter 历程中给予了很多帮忙

  • 然而,当需要逐步简单,你的技术一直精进
  • 屠龙刀这把神兵,或者慢慢的,不太适宜你了
  • 有时候,它甚至制约了你的出招
  • 一位制作神兵的铁匠,在他心中,最好的神兵利器,或者永远是下一把

大部分的状态治理框架,将界面层和逻辑层离开,都是逻辑层来解决界面的刷新;逻辑层能够交给 InheritedWidget 存储管理;阐明,咱们本人也肯定能够存储管理!

  • 本人来治理逻辑层,能够解脱 Element 树的束缚,不必被困在 Element 树的父子节点中
  • 在路由跳转的页面中,能够很轻松的获取上一页面,下一个页面或者上上一个页面逻辑层。

这也是 GetX 中一个核心思想,这并不是一个如许新鲜或浅近技术,然而,我这感觉这是一种思维上的冲破,能够带来更多的可能

依赖注入

阐明

依赖注入有如下实现形式(维基百科):

  • 基于接口。实现特定接口以供内部容器注入所依赖类型的对象。
  • 基于 set 办法。实现特定属性的 public set 办法,来让内部容器调用传入所依赖类型的对象。
  • 基于构造函数。实现特定参数的构造函数,在新建对象时传入所依赖类型的对象。
  • 基于注解。基于 Java 的注解性能,在公有变量前加“@Autowired”等注解,不须要显式的定义以上三种代码,便能够让内部容器传入对应的对象。该计划相当于定义了 public 的 set 办法,然而因为没有真正的 set 办法,从而不会为了实现依赖注入导致裸露了不该裸露的接口(因为 set 办法只想让容器拜访来注入而并不心愿其余依赖此类的对象拜访)。

强耦合类型的,基于构造函数

class Test {
  String msg;

  Test(String msg) {this.msg = msg;}
}

set 形式

class Test {
  String? _msg;

  void setMsg(String msg) {this._msg = msg;}
}

如果在 Java 中,图一时不便,间接在构造函数外面传值,而后须要的值越来越多,导致须要减少该构造函数传参,因为强耦合很多类,一改构造函数,爆红一大片(Dart 构造函数可选参数的个性,就没有这类问题了)

  • Getx 注入的 GetXController 都是由 GetX 框架本人来保护的,如果没有 GetX 这个中间层会是什么样的?

  • 引入 GetX 这个中间层来治理

    • 看下图,霎时就想到了中介者模式
    • 这也是管制反转的思维(创建对象的控制权原本在本人手上,当初交给了第三方)

Put

来看下 GetX 注入的操作

  • put 应用
var controller = Get.put(XxxGetxController());
  • 看看外部操作

    • 哎,各种骚操作
    • 次要逻辑在 Inst 中,Inst 是 GetInterface 的扩大类
class _GetImpl extends GetInterface {}

final Get = _GetImpl();

extension Inst on GetInterface {
  S put<S>(S dependency,
          {String? tag,
          bool permanent = false,
          InstanceBuilderCallback<S>? builder}) =>
      GetInstance().put<S>(dependency, tag: tag, permanent: permanent);
}
  • 次要的逻辑看来还是 GetInstance 中

    • 大家能够看看这中央单例的实现,我发现很多源码都用这种形式写的,十分简洁
    • 全局的数据都是存在 _singl 中,这是个 Map

      • key:对象的 runtimeType 或者类的 Type + tag
      • value:_InstanceBuilderFactory 类,咱们传入 dependedt 对象会存入这个类中
    • _singl 这个 map 存值的时候,不是用的 put,而是用的 putIfAbsent

      • 如果 map 中有 key 和传入 key 雷同的数据,传入的数据将不会被存储
      • 也就是说雷同类实例的对象,传入并不会被笼罩,只会存储第一条数据,后续被放弃
    • 最初应用 find 办法,返回传入的实例
class GetInstance {factory GetInstance() => _getInstance ??= GetInstance._();

  const GetInstance._();

  static GetInstance? _getInstance;

  static final Map<String, _InstanceBuilderFactory> _singl = {};

  S put<S>(
    S dependency, {
    String? tag,
    bool permanent = false,
    @deprecated InstanceBuilderCallback<S>? builder,
  }) {
    _insert(
        isSingleton: true,
        name: tag,
        permanent: permanent,
        builder: builder ?? (() => dependency));
    return find<S>(tag: tag);
  }

  void _insert<S>({
    bool? isSingleton,
    String? name,
    bool permanent = false,
    required InstanceBuilderCallback<S> builder,
    bool fenix = false,
  }) {final key = _getKey(S, name);
    _singl.putIfAbsent(
      key,
      () => _InstanceBuilderFactory<S>(
        isSingleton,
        builder,
        permanent,
        false,
        fenix,
        name,
      ),
    );
  }
    
  String _getKey(Type type, String? name) {return name == null ? type.toString() : type.toString() + name;}
    
  S find<S>({String? tag}) {final key = _getKey(S, tag);
    if (isRegistered<S>(tag: tag)) {if (_singl[key] == null) {if (tag == null) {throw 'Class"$S"is not registered';} else {throw 'Class"$S"with tag"$tag"is not registered';}
      }
      final i = _initDependencies<S>(name: tag);
      return i ?? _singl[key]!.getDependency() as S;} else {
      // ignore: lines_longer_than_80_chars
      throw '"$S" not found. You need to call "Get.put($S())" or "Get.lazyPut(()=>$S())"';
    }
  }
}

find

  • find 办法还是蛮简略的,就是从 map 中取数据的操作
S find<S>({String? tag}) => GetInstance().find<S>(tag: tag);
  • 看下具体逻辑

    • 先判断 _singl 中是否含有该 key 的数据,有则取,无则抛异样
    • 要害代码:_singl[key]!.getDependency() as S,间接通过 key 去 map 取值就行了
class GetInstance {factory GetInstance() => _getInstance ??= GetInstance._();

  const GetInstance._();

  static GetInstance? _getInstance;

  static final Map<String, _InstanceBuilderFactory> _singl = {};
    
  String _getKey(Type type, String? name) {return name == null ? type.toString() : type.toString() + name;}
    
  bool isRegistered<S>({String? tag}) => _singl.containsKey(_getKey(S, tag));
    
  S find<S>({String? tag}) {final key = _getKey(S, tag);
    if (isRegistered<S>(tag: tag)) {if (_singl[key] == null) {if (tag == null) {throw 'Class"$S"is not registered';} else {throw 'Class"$S"with tag"$tag"is not registered';}
      }
      final i = _initDependencies<S>(name: tag);
      return i ?? _singl[key]!.getDependency() as S;} else {
      // ignore: lines_longer_than_80_chars
      throw '"$S" not found. You need to call "Get.put($S())" or "Get.lazyPut(()=>$S())"';
    }
  }
}

GetBuilder 刷新机制

应用

为了常识的连续性,此处简略的写下应用

  • 逻辑层
class GetCounterEasyLogic extends GetxController {
  var count = 0;

  void increase() {
    ++count;
    update();}
}
  • 界面
class GetCounterEasyPage extends StatelessWidget {final GetCounterEasyLogic logic = Get.put(GetCounterEasyLogic());

  @override
  Widget build(BuildContext context) {
    return BaseScaffold(appBar: AppBar(title: const Text('计数器 - 简略式')),
      body: Center(child: GetBuilder<GetCounterEasyLogic>(builder: (logic) {
          return Text('点击了 ${logic.count} 次',
            style: TextStyle(fontSize: 30.0),
          );
        }),
      ),
      floatingActionButton: FloatingActionButton(onPressed: () => logic.increase(),
        child: Icon(Icons.add),
      ),
    );
  }
}

GetBuilder

有一天,我躺在床上思考

  • Obx 的状态治理,GetXController 实例回收是放在路由外面,在很多场景下,存在一些局限性
  • 起初我想到,GetBuilder 应用带泛型,这就能拿到 GetxController 实例,GetBuilder 又是 StatefulWidget
  • 这样就能够应用它来回收实例,能解决很多场景下,GetXController 实例无奈回收的问题(不应用 Getx 路由)
  • 我兴致冲冲的关上 Getx 我的项目,筹备提 PR,而后发现 GetBuilder 曾经在 dispose 外面写了回收实例的操作
  • 淦!

内置回收机制

  • 此处精简很多代码,只展现回收机制的代码
class GetBuilder<T extends GetxController> extends StatefulWidget {
  final GetControllerBuilder<T> builder;
  final bool global;
  final String? tag;
  final bool autoRemove;
  final T? init;

  const GetBuilder({
    Key? key,
    this.init,
    this.global = true,
    required this.builder,
    this.autoRemove = true,
    this.initState,
    this.tag,
  }) : super(key: key);


  @override
  GetBuilderState<T> createState() => GetBuilderState<T>();
}

class GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
    with GetStateUpdaterMixin {
  T? controller;
  bool? _isCreator = false;
  VoidCallback? _remove;
  Object? _filter;

  @override
  void initState() {super.initState();
    widget.initState?.call(this);

    var isRegistered = GetInstance().isRegistered<T>(tag: widget.tag);

    if (widget.global) {if (isRegistered) {controller = GetInstance().find<T>(tag: widget.tag);
      } else {
        controller = widget.init;
        GetInstance().put<T>(controller!, tag: widget.tag);
      }
    } else {
      controller = widget.init;
      controller?.onStart();}

  }

  @override
  void dispose() {super.dispose();
    widget.dispose?.call(this);
    if (_isCreator! || widget.assignId) {if (widget.autoRemove && GetInstance().isRegistered<T>(tag: widget.tag)) {GetInstance().delete<T>(tag: widget.tag);
      }
    }

    _remove?.call();

    controller = null;
    _isCreator = null;
    _remove = null;
    _filter = null;
  }


  @override
  Widget build(BuildContext context) {return widget.builder(controller!);
  }
}

代码里的逻辑还是相当清晰的,initState 获取实例,dispose 回收实例

  1. 通过 GetBuilder 上泛型获取相应 GetXController 实例

    • 不存在:应用 init 传入的实例
    • 存在:间接应用;init 传入的实例有效
  2. autoRemove 能够管制是否主动回收 GetXController 实例

    • 默认为 true:默认开启主动回收
    • true:开启主动回收 false:敞开主动回收

刷新逻辑

  • 这里仅保留刷新逻辑的相干代码,去掉了无需关注的代码
mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> {void getUpdate() {if (mounted) setState(() {});
  }
}

class GetBuilder<T extends GetxController> extends StatefulWidget {
  final GetControllerBuilder<T> builder;
  final bool global;
  final T? init;
  final Object? id;
    
  const GetBuilder({
    Key? key,
    this.init,
    this.id,
    this.global = true,
    required this.builder,
  }) : super(key: key);


  @override
  GetBuilderState<T> createState() => GetBuilderState<T>();
}

class GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
    with GetStateUpdaterMixin {
  T? controller;

  @override
  void initState() {super.initState();
    ...
     
    if (widget.global) {if (isRegistered) {controller = GetInstance().find<T>(tag: widget.tag);
      } else {
        controller = widget.init;
        GetInstance().put<T>(controller!, tag: widget.tag);
      }
    } else {
      controller = widget.init;
      controller?.onStart();}

    _subscribeToController();}

  void _subscribeToController() {_remove?.call();
    _remove = (widget.id == null)
        ? controller?.addListener(_filter != null ? _filterUpdate : getUpdate,)
        : controller?.addListenerId(
            widget.id,
            _filter != null ? _filterUpdate : getUpdate,
          );
  }

  void _filterUpdate() {var newFilter = widget.filter!(controller!);
    if (newFilter != _filter) {
      _filter = newFilter;
      getUpdate();}
  }

  @override
  void didChangeDependencies() {super.didChangeDependencies();
    widget.didChangeDependencies?.call(this);
  }

  @override
  void didUpdateWidget(GetBuilder oldWidget) {super.didUpdateWidget(oldWidget as GetBuilder<T>);
    if (oldWidget.id != widget.id) {_subscribeToController();
    }
    widget.didUpdateWidget?.call(oldWidget, this);
  }

  @override
  Widget build(BuildContext context) {return widget.builder(controller!);
  }
}

关键步骤

  1. 通过泛型获取注入的 GetXController 实例
  2. 增加监听代码

    • addListener:增加监听回调
    • addListenerId:增加监听回调,必须设置 id,update 刷新的时候也必须写上配套的 id
  3. 监听代码:外围代码就是 getUpdate 办法,办法在 GetStateUpdaterMixin 中

    • getUpdate()逻辑就是 setState(),刷新以后 GetBuilder

图示

Update

  • 触发逻辑还是很简略的,应用 update 即可

    • Ids:和下面的 Getbuilder 对应起来了,可刷新对应设置 id 的 GetBuilder
    • condition:是否刷新一个判断条件,默认为 true(假如必须某个 id 大于 3 能力刷新:update([1, 2, 3, 4], index > 3))
abstract class GetxController extends DisposableInterface with ListNotifier {void update([List<Object>? ids, bool condition = true]) {if (!condition) {return;}
    if (ids == null) {refresh();
    } else {for (final id in ids) {refreshGroup(id);
      }
    }
  }
}
  • 看下要害办法 refresh(),在 ListNotifier 类中

    • 能够发现,_updaters 中泛型就是一个办法
    • 在 GetBuilder 中增加的监听就是一个办法参数,办法体外面就是 setState()
    • 齐活了!GetBuilder 增加办法(办法体是 setState),update 遍历触发所有增加办法
typedef GetStateUpdate = void Function();
class ListNotifier implements Listenable {List<GetStateUpdate?>? _updaters = <GetStateUpdate?>[];

  HashMap<Object?, List<GetStateUpdate>>? _updatersGroupIds =
      HashMap<Object?, List<GetStateUpdate>>();

  @protected
  void refresh() {assert(_debugAssertNotDisposed());
    _notifyUpdate();}

  void _notifyUpdate() {for (var element in _updaters!) {element!();
    }
  }

  ...
}
  • 如果在 update 中加了 id 参数,会走 refreshGroup 办法,逻辑和 refresh 简直一样,差异是对 id 的判断:有则执行,无则跳过

    • 遍历所有 ids,而后执行 refreshGroup 办法
abstract class GetxController extends DisposableInterface with ListNotifier {void update([List<Object>? ids, bool condition = true]) {if (!condition) {return;}
    if (ids == null) {refresh();
    } else {for (final id in ids) {refreshGroup(id);
      }
    }
  }
}

class ListNotifier implements Listenable {
  HashMap<Object?, List<GetStateUpdate>>? _updatersGroupIds =
      HashMap<Object?, List<GetStateUpdate>>();

  void _notifyIdUpdate(Object id) {if (_updatersGroupIds!.containsKey(id)) {final listGroup = _updatersGroupIds![id]!;
      for (var item in listGroup) {item();
      }
    }
  }

  @protected
  void refreshGroup(Object id) {assert(_debugAssertNotDisposed());
    _notifyIdUpdate(id);
  }
}

总结

  • 来看下 GetBuilder 刷新图示

Obx 刷新机制

这套刷新机制,和咱们罕用的状态治理框架(provider,bloc)以及下面的 GetBuilder,在应用上有一些区别

  • 变量上:根底类型,实体以及列表之类的数据类型,作者都封装了一套 Rx 类型,快捷在数据后加 obs

    • 例如:RxString msg = “test”.obs(var msg = “test”.obs)
  • 更新上:根底类型间接更新数据就行,实体须要以 .update() 的模式
  • 应用上:应用这类变量,个别要加上 .value,作者也给出一个快捷方式变量前面加个 ()

    • 我不太举荐加 () 的模式,对后续保护我的项目人太不敌对了

Obx 刷新机制,最乏味应该就是变量扭转后,包裹该变量的 Obx 会主动刷新!留神喔,仅仅是包裹该变量的 Obx 会刷新!其它的 Obx 并不会刷新。

这是怎么做到的呢?

  • 实际上,实现起来很简略
  • 然而,如果没有接触过这个思路,恐怕抓破头,都很难想进去,还能这么玩。。。

应用

简略的来看下应用

  • logic
class GetCounterRxLogic extends GetxController {
  var count = 0.obs;

  /// 自增
  void increase() => ++count;}
  • view
class GetCounterRxPage extends StatelessWidget {final GetCounterRxLogic logic = Get.put(GetCounterRxLogic());

  @override
  Widget build(BuildContext context) {
    return BaseScaffold(appBar: AppBar(title: const Text('计数器 - 响应式')),
      body: Center(child: Obx(() {
          return Text('点击了 ${logic.count.value} 次',
            style: TextStyle(fontSize: 30.0),
          );
        }),
      ),
      floatingActionButton: FloatingActionButton(onPressed: () => logic.increase(),
        child: Icon(Icons.add),
      ),
    );
  }
}

Rx 类变量

此处以 RxInt 为例,来看下其外部实现

  • 先来看下整型前面的拓展 obs,这是一个扩大类,0.obs 等同 RxInt(0)
extension IntExtension on int {/// Returns a `RxInt` with [this] `int` as initial value.
  RxInt get obs => RxInt(this);
}
  • 来看下 RxInt:这中央明确了应用 .value 运行时,会主动返回一个以后实例,并批改相应 value 数值
class RxInt extends Rx<int> {RxInt(int initial) : super(initial);

  /// Addition operator.
  RxInt operator +(int other) {
    value = value + other;
    return this;
  }

  /// Subtraction operator.
  RxInt operator -(int other) {
    value = value - other;
    return this;
  }
}
  • 来看下父类 Rx<T>

    • 这中央呈现了一个很重要的类:_RxImpl<T>
class Rx<T> extends _RxImpl<T> {Rx(T initial) : super(initial);

  @override
  dynamic toJson() {
    try {return (value as dynamic)?.toJson();} on Exception catch (_) {throw '$T has not method [toJson]';
    }
  }
}
  • _RxImpl<T> 类继承了 RxNotifier<T> 和 with 了 RxObjectMixin<T>

    • 这个类内容是比拟宏大的,次要是 RxNotifier<T> 和 RxObjectMixin<T> 内容很多
    • 代码很多,先展现下残缺代码;在下一个阐明处会进行简化
abstract class _RxImpl<T> extends RxNotifier<T> with RxObjectMixin<T> {_RxImpl(T initial) {_value = initial;}

  void addError(Object error, [StackTrace? stackTrace]) {subject.addError(error, stackTrace);
  }

  Stream<R> map<R>(R mapper(T? data)) => stream.map(mapper);

  void update(void fn(T? val)) {fn(_value);
    subject.add(_value);
  }

  void trigger(T v) {
    var firstRebuild = this.firstRebuild;
    value = v;
    if (!firstRebuild) {subject.add(v);
    }
  }
}

class RxNotifier<T> = RxInterface<T> with NotifyManager<T>;

mixin NotifyManager<T> {GetStream<T> subject = GetStream<T>();
  final _subscriptions = <GetStream, List<StreamSubscription>>{};

  bool get canUpdate => _subscriptions.isNotEmpty;

  void addListener(GetStream<T> rxGetx) {if (!_subscriptions.containsKey(rxGetx)) {final subs = rxGetx.listen((data) {if (!subject.isClosed) subject.add(data);
      });
      final listSubscriptions =
          _subscriptions[rxGetx] ??= <StreamSubscription>[];
      listSubscriptions.add(subs);
    }
  }

  StreamSubscription<T> listen(void Function(T) onData, {
    Function? onError,
    void Function()? onDone,
    bool? cancelOnError,
  }) =>
      subject.listen(
        onData,
        onError: onError,
        onDone: onDone,
        cancelOnError: cancelOnError ?? false,
      );

  void close() {_subscriptions.forEach((getStream, _subscriptions) {for (final subscription in _subscriptions) {subscription.cancel();
      }
    });

    _subscriptions.clear();
    subject.close();}
}

mixin RxObjectMixin<T> on NotifyManager<T> {
  late T _value;

  void refresh() {subject.add(value);
  }

  T call([T? v]) {if (v != null) {value = v;}
    return value;
  }

  bool firstRebuild = true;

  String get string => value.toString();

  @override
  String toString() => value.toString();

  dynamic toJson() => value;

  @override
  bool operator ==(dynamic o) {if (o is T) return value == o;
    if (o is RxObjectMixin<T>) return value == o.value;
    return false;
  }

  @override
  int get hashCode => _value.hashCode;

  set value(T val) {if (subject.isClosed) return;
    if (_value == val && !firstRebuild) return;
    firstRebuild = false;
    _value = val;

    subject.add(_value);
  }

  T get value {if (RxInterface.proxy != null) {RxInterface.proxy!.addListener(subject);
    }
    return _value;
  }

  Stream<T?> get stream => subject.stream;

  void bindStream(Stream<T> stream) {
    final listSubscriptions =
        _subscriptions[subject] ??= <StreamSubscription>[];
    listSubscriptions.add(stream.listen((va) => value = va));
  }
}
  • 简化 _RxImpl<T>,下面内容太多了,我这中央简化下,把须要关注的内容展现进去:此处有几个须要重点关注的点

    • RxInt 是一个内置 callback 的数据类型(GetStream)
    • RxInt 的 value 变量扭转的时候(set value),会触发 subject.add(_value),外部逻辑是主动刷新操作
    • 获取 RxInt 的 value 变量的时候(get value),会有一个增加监听的操作,这个灰常重要!
abstract class _RxImpl<T> extends RxNotifier<T> with RxObjectMixin<T> {void update(void fn(T? val)) {fn(_value);
    subject.add(_value);
  }
}

class RxNotifier<T> = RxInterface<T> with NotifyManager<T>;

mixin NotifyManager<T> {GetStream<T> subject = GetStream<T>();
  final _subscriptions = <GetStream, List<StreamSubscription>>{};

  bool get canUpdate => _subscriptions.isNotEmpty;

  void addListener(GetStream<T> rxGetx) {if (!_subscriptions.containsKey(rxGetx)) {final subs = rxGetx.listen((data) {if (!subject.isClosed) subject.add(data);
      });
      final listSubscriptions =
          _subscriptions[rxGetx] ??= <StreamSubscription>[];
      listSubscriptions.add(subs);
    }
  }
}

mixin RxObjectMixin<T> on NotifyManager<T> {
  late T _value;

  void refresh() {subject.add(value);
  }

  set value(T val) {if (subject.isClosed) return;
    if (_value == val && !firstRebuild) return;
    firstRebuild = false;
    _value = val;

    subject.add(_value);
  }

  T get value {if (RxInterface.proxy != null) {RxInterface.proxy!.addListener(subject);
    }
    return _value;
  }
}
  • 为啥 GetStream 的 add 会有刷新操作:删了很多代码,保留了重点代码

    • 调用 add 办法时候,会调用 _notifyData 办法
    • _notifyData 办法中,会遍历 _onData 列表,依据条件会执行其泛型的 _data 的办法
    • 我猜,_data 中的办法体,十有八九在某个中央必定增加了 setState()
class GetStream<T> {GetStream({this.onListen, this.onPause, this.onResume, this.onCancel});
  List<LightSubscription<T>>? _onData = <LightSubscription<T>>[];

  FutureOr<void> addSubscription(LightSubscription<T> subs) async {if (!_isBusy!) {return _onData!.add(subs);
    } else {await Future.delayed(Duration.zero);
      return _onData!.add(subs);
    }
  }

  void _notifyData(T data) {
    _isBusy = true;
    for (final item in _onData!) {if (!item.isPaused) {item._data?.call(data);
      }
    }
    _isBusy = false;
  }

  T? _value;

  T? get value => _value;

  void add(T event) {assert(!isClosed, 'You cannot add event to closed Stream');
    _value = event;
    _notifyData(event);
  }
}

typedef OnData<T> = void Function(T data);

class LightSubscription<T> extends StreamSubscription<T> {OnData<T>? _data;}
  • 图示,先来看下,Rx 类具备的性能

    • get value 增加监听
    • set value 执行已增加的监听

Obx 刷新机制

Obx 最大的非凡之处,应该就是应用它的时候,不须要加泛型且能主动刷新,这是怎么做到的呢?

  • Obx:代码并不多,然而皆有妙用

    • Obx 继承 ObxWidget,ObxWidget 实际上也是一个 StatefulWidget
    • _ObxState 中代码就是外围代码了
class Obx extends ObxWidget {
  final WidgetCallback builder;

  const Obx(this.builder);

  @override
  Widget build() => builder();
}


abstract class ObxWidget extends StatefulWidget {const ObxWidget({Key? key}) : super(key: key);

  @override
  _ObxState createState() => _ObxState();

  @protected
  Widget build();}

class _ObxState extends State<ObxWidget> {
  RxInterface? _observer;
  late StreamSubscription subs;

  _ObxState() {_observer = RxNotifier();
  }

  @override
  void initState() {subs = _observer!.listen(_updateTree, cancelOnError: false);
    super.initState();}

  void _updateTree(_) {if (mounted) {setState(() {});
    }
  }

  @override
  void dispose() {subs.cancel();
    _observer!.close();
    super.dispose();}

  Widget get notifyChilds {
    final observer = RxInterface.proxy;
    RxInterface.proxy = _observer;
    final result = widget.build();
    if (!_observer!.canUpdate) {
      throw """
      [Get] the improper use of a GetX has been detected. 
      You should only use GetX or Obx for the specific widget that will be updated.
      If you are seeing this error, you probably did not insert any observable variables into GetX/Obx 
      or insert them outside the scope that GetX considers suitable for an update 
      (example: GetX => HeavyWidget => variableObservable).
      If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
      """;
    }
    RxInterface.proxy = observer;
    return result;
  }

  @override
  Widget build(BuildContext context) => notifyChilds;
}

增加监听

一个控件想刷新,必定有增加监听的逻辑,再在某个中央手动触发

  • 看下_ObxState 类在哪增加监听:只展现监听增加的代码
  • _ObxState 初始化的时候,会实例化一个 RxNotifier() 对象,应用 _observer 变量承受:这个操作很重要
  • initState 中做了一个比拟要害的操作,_observer 的 listener 办法中,将 _updateTree 办法传进去了,这个办法中的逻辑体就是 setState()
class _ObxState extends State<ObxWidget> {
  RxInterface? _observer;
  late StreamSubscription subs;

  _ObxState() {_observer = RxNotifier();
  }

  @override
  void initState() {subs = _observer!.listen(_updateTree, cancelOnError: false);
    super.initState();}

  void _updateTree(_) {if (mounted) {setState(() {});
    }
  }
}

上述很多逻辑和 RxNotifier 类相干,来看下这个类

  • RxNotifier 这个类,外部会实例一个 GetStream<T>() 对象,而后赋值给 subject
  • 下面赋值 _updateTree 办法被传入的 GetStream<T>() 类中,最终增加 _onData 该列表变量中
  • 瞟一眼 _notifyData 办法,是不是遍历执行了 _onData 列表中 item 的办法(item. _data?.call(data))
class RxNotifier<T> = RxInterface<T> with NotifyManager<T>;

mixin NotifyManager<T> {GetStream<T> subject = GetStream<T>();
  final _subscriptions = <GetStream, List<StreamSubscription>>{};

  bool get canUpdate => _subscriptions.isNotEmpty;

  StreamSubscription<T> listen(void Function(T) onData, {
    Function? onError,
    void Function()? onDone,
    bool? cancelOnError,
  }) =>
      subject.listen(
        onData,
        onError: onError,
        onDone: onDone,
        cancelOnError: cancelOnError ?? false,
      );
}

class GetStream<T> {void Function()? onListen;
  void Function()? onPause;
  void Function()? onResume;
  FutureOr<void> Function()? onCancel;

  GetStream({this.onListen, this.onPause, this.onResume, this.onCancel});
  List<LightSubscription<T>>? _onData = <LightSubscription<T>>[];

  FutureOr<void> addSubscription(LightSubscription<T> subs) async {if (!_isBusy!) {return _onData!.add(subs);
    } else {await Future.delayed(Duration.zero);
      return _onData!.add(subs);
    }
  }

  int? get length => _onData?.length;

  bool get hasListeners => _onData!.isNotEmpty;
    
  void _notifyData(T data) {
    _isBusy = true;
    for (final item in _onData!) {if (!item.isPaused) {item._data?.call(data);
      }
    }
    _isBusy = false;
  }

  LightSubscription<T> listen(void Function(T event) onData,
      {Function? onError, void Function()? onDone, bool? cancelOnError}) {
    final subs = LightSubscription<T>(
      removeSubscription,
      onPause: onPause,
      onResume: onResume,
      onCancel: onCancel,
    )
      ..onData(onData)
      ..onError(onError)
      ..onDone(onDone)
      ..cancelOnError = cancelOnError;
    addSubscription(subs);
    onListen?.call();
    return subs;
  }
}
  • 下面代码流程有一点绕,上面画了一个图,心愿对各位有所帮忙

监听转移

在_ObxState 类中做了一个很重要,监听对象转移的操作

_observer 中的对象曾经拿到了 Obx 控件外部的 setState 办法,当初须要将它转移进来啦!

  • 上面贴下将 _observer 中对象转移进来的代码:次要的逻辑就是在 notifyChilds 办法中

    • RxInterface 类中有个 proxy 动态变量,这个变量非常重要,他是一个中转变量!
`class _ObxState extends State<ObxWidget> {
  RxInterface? _observer;

  _ObxState() {_observer = RxNotifier();
  }

  Widget get notifyChilds {
    final observer = RxInterface.proxy;
    RxInterface.proxy = _observer;
    final result = widget.build();
    if (!_observer!.canUpdate) {
      throw """
      [Get] the improper use of a GetX has been detected. 
      You should only use GetX or Obx for the specific widget that will be updated.
      If you are seeing this error, you probably did not insert any observable variables into GetX/Obx 
      or insert them outside the scope that GetX considers suitable for an update 
      (example: GetX => HeavyWidget => variableObservable).
      If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
      """;
    }
    RxInterface.proxy = observer;
    return result;
  }

  @override
  Widget build(BuildContext context) => notifyChilds;
}

abstract class RxInterface<T> {
  bool get canUpdate;

  void addListener(GetStream<T> rxGetx);

  void close();

  static RxInterface? proxy;

  StreamSubscription<T> listen(void Function(T event) onData,
      {Function? onError, void Function()? onDone, bool? cancelOnError});
}

notifyChilds 中的几行代码都有深意,一行行的解读下

  • final observer = RxInterface.proxy:RxInterface.proxy 失常状况为空,然而,可能作为两头变量暂存对象的状况,当初临时将他的对象取出来,存在 observer 变量中
  • RxInterface.proxy = _observer:将咱们在 _ObxState 类中实例化的 RxNotifier() 对象的地址,赋值给了 RxInterface.proxy

    • 留神:这里,RxInterface.proxy 中 RxNotifier() 实例,有以后 Obx 控件的 setState() 办法
  • final result = widget.build():这个赋值相当重要了!调用咱们在内部传进的 Widget

    • 如果这个 Widget 中有响应式变量,那么肯定会调用该变量中获取 get value
    • 还记得 get value 的代码吗?

      mixin RxObjectMixin<T> on NotifyManager<T> {
        late T _value;
          
        T get value {if (RxInterface.proxy != null) {RxInterface.proxy!.addListener(subject);
          }
          return _value;
        }
      }
      
      mixin NotifyManager<T> {GetStream<T> subject = GetStream<T>();
      }
  • 终于建设起分割了,将变量中 GetStream 实例,增加到了 Obx 中的 RxNotifier() 实例;RxNotifier() 实例中有一个 subject(GetStream)实例,Rx 类型中数据变动会触发 subject 变动,最终刷新 Obx

    mixin NotifyManager<T> {GetStream<T> subject = GetStream<T>();
      final _subscriptions = <GetStream, List<StreamSubscription>>{};
    
      bool get canUpdate => _subscriptions.isNotEmpty;
        
      void addListener(GetStream<T> rxGetx) {if (!_subscriptions.containsKey(rxGetx)) {
          // 重点 GetStream 中 listen 办法是用来增加监听办法的,add 的时候会刷新监听办法
          final subs = rxGetx.listen((data) {if (!subject.isClosed) subject.add(data);
          });
          final listSubscriptions =
              _subscriptions[rxGetx] ??= <StreamSubscription>[];
          listSubscriptions.add(subs);
        }
      }
    }
  • if (!_observer!.canUpdate) {}:这个判断就很简略了,如果咱们传入的 Widget 中没有 Rx 类型变量,_subscriptions 数组就会为空,这个判断就会过不了
  • RxInterface.proxy = observer:将 RxInterface.proxy 中原来的值,从新赋给本人,至此 _ObxState 中的 _observer 对象地址,进行了一番奇幻游览后,完结了本人的使命

图示

总结

Obx 的刷新机制,还是蛮有乏味的

  • Rx 变量扭转,主动刷新包裹其变量 Obx 控件,其它的 Obx 控件并不会刷新
  • 应用 Obx 控件,不须要写泛型!牛批!

然而,我认为 Obx 刷新机制,也是有着本身的缺点的,从其实现原理上看,这是无奈防止的

  • 因为 Obx 的主动刷新,必须须要每一个变量都自带监听触发机制;所以,所有的根底类型,实体以及列表,都须要从新封装,这会造成很重大的应用影响:变量的赋值,类型标定,刷新都很失常写法有差别,不相熟该写法的人,看了后,会很好受
  • 因为对所有类型从新封装,通过下面的代码回溯,大家也发现,封装类型的代码相当多;封装类型占用资源必定要比 dart 自带类型的大(这个问题能够防止:封装一个响应式的变量,并不一定须要很多代码,上面我给出了一个封装参考)

手搓一个状态治理框架

GetX 内置了俩套状态管理机制,这边也会依照其刷新机制,手搓俩套出来

我会用极其简略的代码,再现俩套经典的机制

依赖注入

  • 在做刷新机制前,首先必须写一个依赖注入的类,咱们须要本人治理逻辑层的那些实例

    • 我这边写了一个极其简略,仅实现三种根底性能:注入,获取,删除
/// 依赖注入,内部可将实例,注入该类中,由该类治理
class Easy {
  /// 注入实例
  static T put<T>(T dependency, {String? tag}) =>
      _EasyInstance().put(dependency, tag: tag);

  /// 获取注入的实例
  static T find<T>({String? tag, String? key}) =>
      _EasyInstance().find<T>(tag: tag, key: key);

  /// 删除实例
  static bool delete<T>({String? tag, String? key}) =>
      _EasyInstance().delete<T>(tag: tag, key: key);
}

/// 具体逻辑
class _EasyInstance {factory _EasyInstance() => _instance ??= _EasyInstance._();

  static _EasyInstance? _instance;

  _EasyInstance._();

  static final Map<String, _InstanceInfo> _single = {};

  /// 注入实例
  T put<T>(T dependency, {String? tag}) {final key = _getKey(T, tag);
    // 屡次注入会笼罩
    _single[key] = _InstanceInfo<T>(dependency);
    return find<T>(tag: tag);
  }

  /// 获取注入的实例
  T find<T>({String? tag, String? key}) {final newKey = key ?? _getKey(T, tag);
    var info = _single[newKey];

    if (info?.value != null) {return info!.value;} else {throw '"$T" not found. You need to call "Easy.put($T())""';
    }
  }

  /// 删除实例
  bool delete<T>({String? tag, String? key}) {final newKey = key ?? _getKey(T, tag);
    if (!_single.containsKey(newKey)) {print('Instance"$newKey"already removed.');
      return false;
    }

    _single.remove(newKey);
    print('Instance"$newKey"deleted.');
    return true;
  }

  String _getKey(Type type, String? name) {return name == null ? type.toString() : type.toString() + name;}
}

class _InstanceInfo<T> {_InstanceInfo(this.value);

  T value;
}
  • 自定义一个监听类,这个类很重要,上面俩种机制都须要用到
/// 自定义个监听触发类
class EasyXNotifier {List<VoidCallback> _listeners = [];

  void addListener(VoidCallback listener) {_listeners.add(listener);
  }

  void removeListener(VoidCallback listener) {for (final entry in _listeners) {if (entry == listener) {_listeners.remove(entry);
        return;
      }
    }
  }

  void dispose() {_listeners.clear();
  }

  void notify() {if (_listeners.isEmpty) return;

    for (final entry in _listeners) {
      try {entry.call();
      } catch (e) {print(e.toString());
      }
    }
  }
}

EasyBuilder

实现

  • 该模式须要自定义一个基类

    • 我这中央写的极简,相干生命周期都没加,这个加起来也很容易,定义各个生命周期,在 Builder 控件外面触发,就能够了
    • 为了代码简洁,这个暂且不表
class EasyXController {EasyXNotifier xNotifier = EasyXNotifier();

  /// 刷新控件
  void update() {xNotifier.notify();
  }
}
  • 再来看看最外围的 EasyBuilder 控件:这就搞定了!

    • 实现代码写的极其简略,心愿大家思路能有所清晰
/// 刷新控件, 自带回收机制
class EasyBuilder<T extends EasyXController> extends StatefulWidget {final Widget Function(T logic) builder;

  final String? tag;
  final bool autoRemove;

  const EasyBuilder({
    Key? key,
    required this.builder,
    this.autoRemove = true,
    this.tag,
  }) : super(key: key);

  @override
  _EasyBuilderState<T> createState() => _EasyBuilderState<T>();
}

class _EasyBuilderState<T extends EasyXController>
    extends State<EasyBuilder<T>> {
  late T controller;

  @override
  void initState() {super.initState();

    controller = Easy.find<T>(tag: widget.tag);
    controller.xNotifier.addListener(() {if (mounted) setState(() {});
    });
  }

  @override
  void dispose() {if (widget.autoRemove) {Easy.delete<T>(tag: widget.tag);
    }
    controller.xNotifier.dispose();

    super.dispose();}

  @override
  Widget build(BuildContext context) {return widget.builder(controller);
  }
}

应用

  • 应用很简略,先看下逻辑层
class EasyXCounterLogic extends EasyXController {
  var count = 0;

  void increase() {
    ++count;
    update();}
}
  • 界面层
class EasyXCounterPage extends StatelessWidget {final EasyXCounterLogic logic = Easy.put(EasyXCounterLogic());

  @override
  Widget build(BuildContext context) {
    return BaseScaffold(appBar: AppBar(title: const Text('EasyX- 自定义 EasyBuilder 刷新机制')),
      body: Center(child: EasyBuilder<EasyXCounterLogic>(builder: (logic) {
          return Text('点击了 ${logic.count} 次',
            style: TextStyle(fontSize: 30.0),
          );
        }),
      ),
      floatingActionButton: FloatingActionButton(onPressed: () => logic.increase(),
        child: Icon(Icons.add),
      ),
    );
  }
}
  • 效果图

Ebx:主动刷新机制

主动刷新机制,因为没加泛型,所以无奈确定本人外部应用了哪个注入实例,Getx 中是在路由外面去回收这些实例的,然而,如果你没应用 GetX 的路由,又用 Obx,你会发现,GetXController 竟然无奈主动回收!!!

此处针对该场景,我会给出一种解决方案

实现

  • 在主动刷新的机制中,须要将根底类型进行封装

    • 次要逻辑在 Rx<T> 中
    • set value 和 get value 是要害
/// 拓展函数
extension IntExtension on int {RxInt get ebs => RxInt(this);
}

extension StringExtension on String {RxString get ebs => RxString(this);
}

extension DoubleExtension on double {RxDouble get ebs => RxDouble(this);
}

extension BoolExtension on bool {RxBool get ebs => RxBool(this);
}

/// 封装各类型
class RxInt extends Rx<int> {RxInt(int initial) : super(initial);

  RxInt operator +(int other) {
    value = value + other;
    return this;
  }

  RxInt operator -(int other) {
    value = value - other;
    return this;
  }
}

class RxDouble extends Rx<double> {RxDouble(double initial) : super(initial);

  RxDouble operator +(double other) {
    value = value + other;
    return this;
  }

  RxDouble operator -(double other) {
    value = value - other;
    return this;
  }
}

class RxString extends Rx<String> {RxString(String initial) : super(initial);
}

class RxBool extends Rx<bool> {RxBool(bool initial) : super(initial);
}

/// 主体逻辑
class Rx<T> {EasyXNotifier subject = EasyXNotifier();

  Rx(T initial) {_value = initial;}

  late T _value;

  bool firstRebuild = true;

  String get string => value.toString();

  @override
  String toString() => value.toString();

  set value(T val) {if (_value == val && !firstRebuild) return;
    firstRebuild = false;
    _value = val;

    subject.notify();}

  T get value {if (RxEasy.proxy != null) {RxEasy.proxy!.addListener(subject);
    }
    return _value;
  }
}
  • 须要写一个十分重要的直达类,这个也会贮存响应式变量的监听对象

    • 这个类是有着十分外围的逻辑:他将响应式变量和刷新控件关联起来了!
class RxEasy {EasyXNotifier easyXNotifier = EasyXNotifier();

  Map<EasyXNotifier, String> _listenerMap = {};

  bool get canUpdate => _listenerMap.isNotEmpty;

  static RxEasy? proxy;

  void addListener(EasyXNotifier notifier) {if (!_listenerMap.containsKey(notifier)) {
      // 重要:将 Ebx 中的监听对象转换到此处
      easyXNotifier = proxy!.easyXNotifier;
      // 变量监听中刷新
      notifier.addListener(() {
        // 刷新 ebx 中增加的监听
        easyXNotifier.notify();});
      // 增加进入 map 中
      _listenerMap[notifier] = '';
    }
  }
}
  • 刷新控件 Ebx
typedef WidgetCallback = Widget Function();

class Ebx extends StatefulWidget {const Ebx(this.builder, {Key? key}) : super(key: key);

  final WidgetCallback builder;

  @override
  _EbxState createState() => _EbxState();
}

class _EbxState extends State<Ebx> {RxEasy _rxEasy = RxEasy();

  @override
  void initState() {super.initState();

    _rxEasy.easyXNotifier.addListener(() {if (mounted) setState(() {});
    });
  }

  Widget get notifyChild {
    final observer = RxEasy.proxy;
    RxEasy.proxy = _rxEasy;
    final result = widget.builder();
    if (!_rxEasy.canUpdate) {throw 'Widget lacks Rx type variables';}
    RxEasy.proxy = observer;
    return result;
  }

  @override
  Widget build(BuildContext context) {return notifyChild;}

  @override
  void dispose() {_rxEasy.easyXNotifier.dispose();

    super.dispose();}
}
  • 在下面说了,在主动刷新机制中,主动回收依赖实例是个蛋筒的问题,此处我写了一个回收控件,能够解决此问题

    • 应用时,必须套一层了;如果大家有更好的思路,麻烦在评论里告知
class EasyBindWidget extends StatefulWidget {
  const EasyBindWidget({
    Key? key,
    this.bind,
    this.tag,
    this.binds,
    this.tags,
    required this.child,
  })  : assert(
          binds == null || tags == null || binds.length == tags.length,
          'The binds and tags arrays length should be equal\n'
          'and the elements in the two arrays correspond one-to-one',
        ),
        super(key: key);

  final Object? bind;
  final String? tag;

  final List<Object>? binds;
  final List<String>? tags;

  final Widget child;

  @override
  _EasyBindWidgetState createState() => _EasyBindWidgetState();
}

class _EasyBindWidgetState extends State<EasyBindWidget> {
  @override
  Widget build(BuildContext context) {return widget.child;}

  @override
  void dispose() {_closeController();
    _closeControllers();

    super.dispose();}

  void _closeController() {if (widget.bind == null) {return;}

    var key = widget.bind.runtimeType.toString() + (widget.tag ?? '');
    Easy.delete(key: key);
  }

  void _closeControllers() {if (widget.binds == null) {return;}

    for (var i = 0; i < widget.binds!.length; i++) {var type = widget.binds![i].runtimeType.toString();

      if (widget.tags == null) {Easy.delete(key: type);
      } else {var key = type + (widget.tags?[i] ?? '');
        Easy.delete(key: key);
      }
    }
  }
}

应用

  • 逻辑层,这次,咱们连基类都不须要写
class EasyXEbxCounterLogic {
  RxInt count = 0.ebs;

  /// 自增
  void increase() => ++count;}
  • 界面层:页面顶节点套了一个 EasyBindWidget,能够保障依赖注入实例能够主动回收
class EasyXEbxCounterPage extends StatelessWidget {final EasyXEbxCounterLogic logic = Easy.put(EasyXEbxCounterLogic());

  @override
  Widget build(BuildContext context) {
    return EasyBindWidget(
      bind: logic,
      child: BaseScaffold(appBar: AppBar(title: const Text('EasyX- 自定义 Ebx 刷新机制')),
        body: Center(child: Ebx(() {
            return Text('点击了 ${logic.count.value} 次',
              style: TextStyle(fontSize: 30.0),
            );
          }),
        ),
        floatingActionButton: FloatingActionButton(onPressed: () => logic.increase(),
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}
  • 效果图

总结

这俩种刷新模式,含金量高的,应该还是主动刷新的机制,思路很乏味,响应式变量和刷新控件通过动态变量的模式建设起分割,cool!又是一种骚操作!

这俩套状态管理机制,我都给出了对依赖注入对象,主动回收的解决方案,心愿对大家的思路有所启迪。

最初

终于把最初一篇 GetX 的原理分析写完了(只针对 GetX 状态治理这部分内容),了了一桩心事。。。

  • 有些流程比拟绕,顺便画了一些图,图文并茂总会让人情绪愉悦嘛 ……

如果大家认真看完了整片文章,可能会发现:状态治理 + 依赖注入,能够使得应用场景大大的被拓展

  • GetBuilder 的主动回收就是借助依赖注入,无缝获取注入实例,从而实现主动回收的操作
  • 而且 GetBuilder 还无需额定传参数!

整篇文章写下来,我感觉真的尽力了

  • 从 InheritedWidget 到路由
  • 而后到依赖注入
  • 再就是俩种状态框架原理分析
  • 最初根据俩种刷新机制,手搓俩套状态治理框架

也算是层层递进的将其中的常识,一点点的展现在大家的背后,心愿能够帮到各位!!!

系列文章 + 相干地址

  • 文章中 Demo 的 Github 地址:flutter_use
  • Flutter GetX 应用 — 简洁的魅力!
  • 源码篇:Flutter Bloc 背地的思维,一篇纠结的文章
  • 源码篇:Flutter Provider 的另一面(万字图文 + 插件)

正文完
 0