目录
- StatefulElement
- 1. 构造函数
- 2. build
- 3. _firstBuild
- 3. didChangeDependencies
- 4. setState
- InheritedElement
- 1. Element类
- 2. _updateInheritance
- 3. InheritedWidget数据向下传递
- 3.1 dependOnInheritedWidgetOfExactType
- 4. InheritedWidget的状态绑定
- 4.1. ProxyElement
在flutter项目中,StatelessWidget,StatefulWidget,InheritedWidget是常见的widget,今天通过源码分析下它们是怎么实现的。
对应的功能基本上都是在element中实现的,widget只是提供组件配置的作用,所以在讲解StatefulWidget,InheritedWidget的时候,主要还是分析对应的element的实现。
StatefulElement
StatefulWidget是带有状态的Widget,和statelessWidget不同,Widget的创建是委托给state创建的,而不是使用widget.build直接创建的。
StatelessWidget代码上一章 三棵树的建立过程 已经讲过了,忽略。
) )
1. 构造函数
对比statelessElement的构造函数:
class StatefulElement extends ComponentElement {/// Creates an element that uses the given widget as its configuration.StatefulElement(StatefulWidget widget): _state = widget.createState(),super(widget) {assert(state._element == null);state._element = this;state._widget = widget;assert(state._debugLifecycleState == _StateLifecycle.created);}//...省略
}
在构造函数中,直接调用了widget.createState()
.新建了state,state的成员变量有element,widget都是私有成员,这个时候state的生命周期应该是created的,state._debugLifecycleState == _StateLifecycle.created
2. build
使用state来创建widget:
Widget build() => state.build(this);
3. _firstBuild
void _firstBuild() {//...省略state.didChangeDependencies();assert(() {state._debugLifecycleState = _StateLifecycle.ready;}());super._firstBuild();}
这个函数调用,发生在第一次生成element的时候,该element被mount的时候触发,这个时候会回调state的didChangeDependencies方法。
还有一种情况是performRebuild()的时候有可能会回调,下面会讲到。
再看这幅图:
3. didChangeDependencies
didChangeDependencies函数是element的回调接口,这个接口是在依赖项更改的时候被parent通知调用的,会修改_didChangeDependencies = true;
,然后performRebuild()函数会触发state.didChangeDependencies();
的回调。
bool _didChangeDependencies = false;void didChangeDependencies() {super.didChangeDependencies();_didChangeDependencies = true;}void performRebuild() {if (_didChangeDependencies) {state.didChangeDependencies();_didChangeDependencies = false;}super.performRebuild();}
4. setState
void setState(VoidCallback fn) {//。。。省略final Object? result = fn() as dynamic;assert(() {if (result is Future) {throw FlutterError.fromParts(<DiagnosticsNode>[]);}return true;}());_element!.markNeedsBuild();}
可以看到setstate的参数不能是返回future的回调,然后调用element的markNeedsBuild方法,通知element重新build一次。
重新build的时候,如果满足element可以复用旧的,但是需要更新newWidget的情况下,会触发state.didUpdateWidget(
方法,也就对应的上图生命周期了。
注:上一章 三棵树的建立过程 已经讲过其他的函数,这里忽略。
InheritedElement
InheritedWidget本质有两大功能,
- InheritedWidget数据向下传递(下层节点可以获取到InheritedWidget中的数据)
- InheritedWidget的状态绑定(就是InheritedWidget被修改,会导致引用的地方数据刷新)
这些功能都是在Element和InheritedElement中实现的。
1. Element类
element是基类,是所有子类的基础实现,它内部有这几个成员变量让inheritedElement功能得以实现:
PersistentHashMap<Type, InheritedElement>? _inheritedElements;Set<InheritedElement>? _dependencies;bool _hadUnsatisfiedDependencies = false;
_inheritedElements这个保存着这棵树的所有inheritedElement类型元素(如果自己也是,那么自己也会加入到这个集合中);
_dependencies保存着当前element所依赖的祖先InheritedElement;
_hadUnsatisfiedDependencies 当按照类型查找祖先InheritedElement没找到,那么这个变量会设置成true,下次active激活页面的时候,会通知build一次。
2. _updateInheritance
element的实现如下:
void mount(Element? parent, Object? newSlot) {//省略_updateInheritance();attachNotificationTree();}void _updateInheritance() {assert(_lifecycleState == _ElementLifecycle.active);_inheritedElements = _parent?._inheritedElements;}
在当前element挂载到树中的时候,会主动更新一次_inheritedElements 元素,从parent当中获取_inheritedElements 集合。
InheritedElement复写了这个方法:
void _updateInheritance() {assert(_lifecycleState == _ElementLifecycle.active);final PersistentHashMap<Type, InheritedElement> incomingWidgets =_parent?._inheritedElements ?? const PersistentHashMap<Type, InheritedElement>.empty();_inheritedElements = incomingWidgets.put(widget.runtimeType, this);}
从parent获取了_inheritedElements 集合之后,还需要将自己加入到这个集合中。
3. InheritedWidget数据向下传递
3.1 dependOnInheritedWidgetOfExactType
dependOnInheritedWidgetOfExactType这个方法,通常使用的情况是,子widget的state中去获取全局的element。如下,获取这个主题元素CupertinoThemeData 的时候,调用了of方法,里面就是调用的dependOnInheritedWidgetOfExactType。
static CupertinoThemeData of(BuildContext context) {final _InheritedCupertinoTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedCupertinoTheme>();return (inheritedTheme?.theme.data ?? const CupertinoThemeData()).resolveFrom(context);}
然后在系统的buttonWidget中使用了这个主题:
我们接着看下dependOnInheritedWidgetOfExactType到底做了什么事情:
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {assert(_debugCheckStateIsActiveForAncestorLookup());final InheritedElement? ancestor = _inheritedElements == null ? null : _inheritedElements![T];if (ancestor != null) {return dependOnInheritedElement(ancestor, aspect: aspect) as T;}_hadUnsatisfiedDependencies = true;return null;}
- 从_inheritedElements集合中,按照key来查询element元素;
- 查找到目标ancestor之后,需要和祖先进行关系绑定
dependOnInheritedElement
; - 如果查找不到,那么将_hadUnsatisfiedDependencies 置为true。
dependOnInheritedElement
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {_dependencies ??= HashSet<InheritedElement>();_dependencies!.add(ancestor);ancestor.updateDependencies(this, aspect);return ancestor.widget as InheritedWidget;}
首先是将祖先element加入到_dependencies集合中,这样该element就知道自己依赖了哪几个祖先element,祖先也调用updateDependencies更新祖先的依赖,然后将祖先返回。
updateDependencies
祖先是inheritedElement,它实现了updateDependencies方法,将子element加入到map中。
final Map<Element, Object?> _dependents = HashMap<Element, Object?>();void updateDependencies(Element dependent, Object? aspect) {setDependencies(dependent, null);}void setDependencies(Element dependent, Object? value) {_dependents[dependent] = value;}
通过上面的步骤,子element和祖先inheritedElement,产生了相互依赖关系。
实现了InheritedWidget数据向下传递(下层节点可以获取到InheritedWidget中的数据)
4. InheritedWidget的状态绑定
4.1. ProxyElement
proxyElement复写了两个接口:
widget的创建是返回的子child
Widget build() => (widget as ProxyWidget).child;
update更新widget,需要先调用updated,在调用build方法
void update(ProxyWidget newWidget) {final ProxyWidget oldWidget = widget as ProxyWidget;super.update(newWidget);updated(oldWidget);rebuild(force: true);}
proxyElement新增了两个接口:
//widget已经更新过了,需要通知依赖项void updated(covariant ProxyWidget oldWidget) {notifyClients(oldWidget);}//通知接口,具体需要子类实现void notifyClients(covariant ProxyWidget oldWidget);
inheritedElement复写接口:
void updated(InheritedWidget oldWidget) {if ((widget as InheritedWidget).updateShouldNotify(oldWidget)) {super.updated(oldWidget);}}void notifyClients(InheritedWidget oldWidget) {assert(_debugCheckOwnerBuildTargetExists('notifyClients'));for (final Element dependent in _dependents.keys) {assert(() {// check that it really is our descendantElement? ancestor = dependent._parent;while (ancestor != this && ancestor != null) {ancestor = ancestor._parent;}return ancestor == this;}());// check that it really depends on usassert(dependent._dependencies!.contains(this));notifyDependent(oldWidget, dependent);}}
- 先判断updateShouldNotify接口需要根据widget来判断,是否通知依赖集合
- 如果需要通知,那么遍历_dependents集合,首先验证是否是自己的子孙,接着验证子element._dependencies集合是否有parent;
- 接着通知依赖的子项,调用
dependent.didChangeDependencies();
element实现:
void didChangeDependencies() {assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-opassert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));markNeedsBuild();}
通知子child的element需要重新build了,因为子child依赖了parent的数据,parent的数据发生变化的时候,是需要强制子child去重新build的。
statefulElemenr实现:
bool _didChangeDependencies = false;void didChangeDependencies() {super.didChangeDependencies();_didChangeDependencies = true;}
除了调用super方法,还将_didChangeDependencies 设置为true;上面说过performRebuild()
调用的时候,如果这个标志是true的话,会通知state.didChangeDependencies();
接口,对应上了上面图片所示的生命周期函数回调。
inheritedElement功能流程图: