人心中的成见是一座大山,任你怎么努力都休想搬动。
这是电影《哪吒》里申公豹说的一句话,也是贯彻整部电影的一个主题;或许这句话引起了太多人的共鸣: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() 这个方法
代码很简单,就是从 _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; } }整体上逻辑还是比较清晰
遇到某个节点为 InheritedWidget 时,会将父节点的 _inheritedWidgets 变量给 incomingWidgets 这个临时变量
incomingWidgets 为空:为父类Element的 _inheritedWidgets 变量, 实例了一个map对象