人心中的成见是一座大山,任你怎么努力都休想搬动。
这是电影《哪吒》里申公豹说的一句话,也是贯彻整部电影的一个主题;或许这句话引发了太多人的共鸣:35岁职场危机,大厂卡本科学历,无房无车结婚难等等,因此,这句话也常常被人提起。git
同时,由于GetX做者的一些言论,也让一些成见一直伴随着GetX这个框架。github
我写这篇文章,并非为GetX正名segmentfault
GetX总体设计,有很多优秀点思想,我但愿将这些优秀设计思路展示给你们;或许会对你设计本身的框架有一些帮助,同时也是对本身思考历程的一个记录。数组
在说GetX设计思想以前,须要先介绍几个知识,在Flutter茁壮发展的历程里,他们都留下了浓墨重彩的一笔
不得不说,这个控件真的是一个神奇控件,它就仿佛是一把神兵利器app
InheritedWidget这把神兵藏有什么?框架
InheritedWidget是咱们统称的一个控件名,精髓仍是InheritedElement,InheritedWidget的数据传递,来看下存和取这俩个过程less
存数据
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 = '传输数据'; }
取数据
var transferDataElement = context.getElementForInheritedWidgetOfExactType<TransferDataWidget>() as TransferDataElement?; var msg = transferDataElement.value;
能够发现,咱们只须要经过Element的getElementForInheritedWidgetOfExactType方法,就能够拿到父节点的TransferDataElement实例(必须继承InheritedElement)async
拿到实例后,天然就能够很简单的拿到相应数据了ide
原理
能够发现咱们是拿到了XxxInheritedElement实例,继而拿到了储存的值,因此关键在 getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() 这个方法函数
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; } ... }
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; } }
总体上逻辑仍是比较清晰
遇到某个节点为 InheritedWidget 时,会将父节点的 _inheritedWidgets 变量给 incomingWidgets 这个临时变量
为何任何一个Widget的Element实例的 _inheritedWidgets 变量,可直接拿到父节点InheritedElement实例?
abstract class Element extends DiagnosticableTree implements BuildContext { Map<Type, InheritedElement>? _inheritedWidgets; void _updateInheritance() { assert(_lifecycleState == _ElementLifecycle.active); _inheritedWidgets = _parent?._inheritedWidgets; } ... }
InheritedElement和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
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 方法
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该方法的使用也很简单
// 举例 var inheritedElement = context .getElementForInheritedWidgetOfExactType<ChangeNotifierEasyP<T>>() as EasyPInheritedElement<T>?; context.dependOnInheritedElement(inheritedElement);
Provider核心原理,就是采用了InheritedWidget这种刷新机制
想详细了解Provider相关原理,可参考下面文章
图示
你们在使用InheritedWidget获取数据的时候,或许有过这样一种困扰:A页面 ---> B页面 ---> C页面
若是我在A页面使用InheritedWidget储存了数据,跳转到B页面或者C页面,会发现使用context获取不到A页面的InheritedElement
这侧面证实了Navigator路由跳转:A页面跳转B页面,B页面并非A页面的子节点
这里我画了下大体结构,若有误差,请务必指出来,我会尽快修改
关于Flutter路由原理解析,可参考此文章(做者为啥如今不更文了呢 ~~):Flutter 路由原理解析
InheritedWidget为咱们带了不少便利
可是,Element提供的获取InheritedElement的方式,终究和路由机制没法很好的结合;这也模块设计没法避免的事情,或许某些模块设计的最优解,很难顾忌到其它模快的一些机制
InheritedWidget这把神兵利器,在咱们学习Flutter历程中给予了不少帮助
大部分的状态管理框架,将界面层和逻辑层分开,都是逻辑层来处理界面的刷新;逻辑层能够交给InheritedWidget存储管理;说明,咱们本身也必定能够存储管理!
这也是GetX中一个核心思想,这并非一个多么新颖或高深技术,可是,我这以为这是一种思惟上的突破,能够带来更多的可能
依赖注入有以下实现方式(维基百科):
强耦合类型的,基于构造函数
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这个中间层来管理
来看下GetX注入的操做
var controller = Get.put(XxxGetxController());
看看内部操做
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
_singl 这个map存值的时候,不是用的put,而是用的putIfAbsent
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())"'; } } }
S find<S>({String? tag}) => GetInstance().find<S>(tag: tag);
看下具体逻辑
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())"'; } } }
为了知识的连续性,此处简单的写下使用
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), ), ); } }
有一天,我躺在床上思考
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回收实例
经过GetBuilder上泛型获取相应GetXController实例
autoRemove能够控制是否自动回收GetXController实例
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!); } }
关键步骤
添加监听代码
监听代码:核心代码就是getUpdate方法,方法在 GetStateUpdaterMixin 中
图示
触发逻辑仍是很简单的,使用update便可
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类中
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的判断:有则执行,无则跳过
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); } }
这套刷新机制,和咱们经常使用的状态管理框架(provider,bloc)以及上面的GetBuilder,在使用上有一些区别
变量上:基础类型,实体以及列表之类的数据类型,做者都封装了一套Rx类型,快捷在数据后加obs
使用上:使用这类变量,通常要加上 .value ,做者也给出一个快捷方式变量后面加个 ()
Obx刷新机制,最有趣应该就是变量改变后,包裹该变量的Obx会自动刷新!注意喔,仅仅是包裹该变量的Obx会刷新!其它的Obx并不会刷新。
这是怎么作到的呢?
简单的来看下使用
class GetCounterRxLogic extends GetxController { var count = 0.obs; ///自增 void increase() => ++count; }
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), ), ); } }
此处以 RxInt 为例,来看下其内部实现
extension IntExtension on int { /// Returns a `RxInt` with [this] `int` as initial value. RxInt get obs => RxInt(this); }
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>
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>
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>,上面内容太多了,我这地方简化下,把须要关注的内容展现出来:此处有几个须要重点关注的点
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会有刷新操做:删了不少代码,保留了重点代码
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类具备的功能
Obx最大的特殊之处,应该就是使用它的时候,不须要加泛型且能自动刷新,这是怎么作到的呢?
Obx:代码并很少,可是皆有妙用
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; }
一个控件想刷新,确定有添加监听的逻辑,再在某个地方手动触发
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 类相关,来看下这个类
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 方法中
`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中的几行代码都有深意,一行行的解读下
RxInterface.proxy = _observer:将咱们在 _ObxState类中实例化的 RxNotifier() 对象的地址,赋值给了RxInterface.proxy
final result = widget.build():这个赋值至关重要了!调用咱们在外部传进的Widget
还记得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); } } }
图示
Obx的刷新机制,仍是蛮有有趣的
可是,我认为Obx刷新机制,也是有着自身的缺陷的,从其实现原理上看,这是没法避免的
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.putIfAbsent(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()); } } } }
该模式须要自定义一个基类
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), ), ); } }
自动刷新机制,由于没加泛型,因此没法肯定本身内部使用了哪一个注入实例,Getx中是在路由里面去回收这些实例的,可是,若是你没使用GetX的路由,又用Obx,你会发现,GetXController竟然没法自动回收!!!
此处针对该场景,我会给出一种解决方案
在自动刷新的机制中,须要将基础类型进行封装
///拓展函数 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)) { //变量监听中刷新 notifier.addListener(() { //刷新ebx中添加的监听 easyXNotifier.notify(); }); //添加进入map中 _listenerMap[notifier] = ''; } } }
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; }
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状态管理这部份内容),了了一桩心事。。。
若是你们认真看完了整片文章,可能会发现:状态管理+依赖注入,可使得使用场景大大的被拓展
整篇文章写下来,我真的尽力了
也算是层层递进的将其中的知识,一点点的展现在你们的面前,但愿能够帮到各位!!!
系列文章 + 相关地址