在开始讲解这篇文章之前,如果没看过前几篇的文章的话,建议先阅读以下。
Flutter源码分析(一)先从StatelessWidget开始
Flutter源码分析(二)进入StatefulWidget世界
2020年个人完成了一个开源的Flutter的影视项目如下。
Flutter 基于Provider+MVVM的Flutter 视频App项目
对ProxyWidget这个类不是很熟悉的同学,那么它的子类InheritedWidget应该是知道的吧。它是Flutter Widget中的灵魂设定之一,因为InheritedWidget常被用于做数据共享。比如常见的Theme/ThemeData, Text/DefaultTextStyle等等都是通过InheritedWidget 来实现的数据共享,并且Flutter中的状态管理框架也都是通过它实现的,例如最为知名其中之一的状态管理框架Provider。
ProxyWidget
ProxyWidget是InheritedWidget的父类,那么它是干什么的呢?ProxyWidget的说明,可以看到源码注释上写了这样一句话:A widget that has a child widget provided to it, instead of building a new widget.其意思为这是一个提供子部件的部件,而不是构建新的部件。
ProxyWidget源码如下:
abstract class ProxyWidget extends Widget {
/// Creates a widget that has exactly one child widget.
const ProxyWidget({ Key key, @required this.child }) : super(key: key);
/// The widget below this widget in the tree.
///
/// {@template flutter.widgets.child}
/// This widget can only have one child. To lay out multiple children, let this
/// widget's child be a widget such as [Row], [Column], or [Stack], which have a
/// `children` property, and then provide the children to that widget.
/// {@endtemplate}
final Widget child;
}
可以看出ProxyWidget这个抽象类足够的简单,它内部仅有一个child的属性。那么接着往下看它的继承关系。总共有两个子类InheritedWidget和ParentDataWidget分别是ProxyWidget的子类。阅读过前几篇文章的同学们都应该清楚了,说完widget就该说Element了,因为Element才是真正提供渲染的方法widget只是它的配置项而已。
ProxyElement
先来看一下它的源码:/// An [Element] that uses a [ProxyWidget] as its configuration.
abstract class ProxyElement extends ComponentElement {
/// Initializes fields for subclasses.
ProxyElement(ProxyWidget widget) : super(widget);
@override
ProxyWidget get widget => super.widget as ProxyWidget;
@override
Widget build() => widget.child;
@override
void update(ProxyWidget newWidget) {
final ProxyWidget oldWidget = widget;
assert(widget != null);
assert(widget != newWidget);
super.update(newWidget);
assert(widget == newWidget);
updated(oldWidget);
_dirty = true;
rebuild();
}
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
element.
@protected
void notifyClients(covariant ProxyWidget oldWidget);
}
可以看到总共就提供了三个方法,update, updated, notifyClients。
update方法先会调用updated方法之后标记肮脏执行rebuild方法。
updated方法会去调用notifyClient方法。一般子类会去重写它来实现自己的逻辑之后再调用父类的updated方法。(接下来分析子类的时候会提及到)
notifyClients方法是一个抽象方法,由子类去实现。(接下来分析子类的时候会提及到)
InheritedWidget
InheritedWidget是ProxyWidget实现的子类,在Flutter当中的地位也像上边说的那样举足轻重。那来先看一下源码如下:
abstract class InheritedWidget extends ProxyWidget {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const InheritedWidget({ Key key, Widget child })
: super(key: key, child: child);
@override
InheritedElement createElement() => InheritedElement(this);
/// Whether the framework should notify widgets that inherit from this widget.
///
/// When this widget is rebuilt, sometimes we need to rebuild the widgets that
/// inherit from this widget but sometimes we do not. For example, if the data
/// held by this widget is the same as the data held by `oldWidget`, then we
/// do not need to rebuild the widgets that inherited the data held by
/// `oldWidget`.
///
/// The framework distinguishes these cases by calling this function with the
/// widget that previously occupied this location in the tree as an argument.
/// The given widget is guaranteed to have the same [runtimeType] as this
/// object.
@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
内部相当的简单,首先必不可少的就是会创建它与之对应的Element,之后会提供一个抽象方法updateShouldNotify。这个方法是用来控制是否进行数据同步的,也就是说子组件在引用到InheritedWidget的父组件的值的时候,其值改变是否需要进行子组件的重新build。
InheritedElement
在文章开头我们就说过InheritedWidget的作用用来数据共享的。那么数据共享是什么意思呢?任意一个子组件都可以访问它的数据进行数据共享。当InheritedWidget内部数据进行更新的时候,可以实现所有访问它数据的子组件进行同步更新。那它是如何来实现上述功能的呢?我们来通过源码进行切入的了解一下: InheritedElement的源码如下:class InheritedElement extends ProxyElement {
/// Creates an element that uses the given widget as its configuration.
InheritedElement(InheritedWidget widget) : super(widget);
@override
InheritedWidget get widget => super.widget as InheritedWidget;
final Map<Element, Object> _dependents = HashMap<Element, Object>();
@override
void _updateInheritance() {
assert(_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;
}
@override
void debugDeactivated() {
assert(() {
assert(_dependents.isEmpty);
return true;
}());
super.debugDeactivated();
}
@protected
Object getDependencies(Element dependent) {
return _dependents[dependent];
}
@protected
void setDependencies(Element dependent, Object value) {
_dependents[dependent] = value;
}
@protected
void updateDependencies(Element dependent, Object aspect) {
setDependencies(dependent, null);
}
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
@override
void notifyClients(InheritedWidget oldWidget) {
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (final Element dependent in _dependents.keys) {
assert(() {
// check that it really is our descendant
Element ancestor = dependent._parent;
while (ancestor != this && ancestor != null)
ancestor = ancestor._parent;
return ancestor == this;
}());
// check that it really depends on us
assert(dependent._dependencies.contains(this));
notifyDependent(oldWidget, dependent);
}
}
}
我们会看到“_inheritedWidgets”这个map,它其实在父类Element里边的,我们在讲解
Flutter源码分析(一)先从StatelessWidget开始 就有说过。那么它在InheritedElement里使用是干什么呢?通过源码可以看出来就是为了保存当前InheritedWidget对象来着。为什么要保存呢?主要还是为了可以通过子组件的context来进行找到父类的InheritedWidget。
我们想使用InheritedWiget里边的数据的时候,flutter为我们他提供了几个方法,其中有一个方法就是“context.getElementForInheritedWidgetOfExactType()”。它就是需要通过_inheritedWidgets来获取到想要的父组件InheritedWidget对象的。
获取父组件InheritedWidget对象
获取父组件InheritedWidget对象,一般通常有两个获取方式。一、dependOnInheritedWidgetOfExactType
二、getElementForInheritedWidgetOfExactType
它们两者的区别是什么呢?通过命名可以分析大概出来,一个是“依赖”,一个是“得到”。简明的说就是调用“dependOnInheritedWidgetOfExactType”来获取InheritedWidget对象的时候修改内部数据,这个被修改的数据被其他组件引用会自动被同步,使得被引用这个数据的其他子组件都可以保持一致最新的数据。而调用“getElementForInheritedWidgetOfExactType”来获取InheritedWidget对象的时候修改内部数据并不会同步更新。
先来看一下“getElementForInheritedWidgetOfExactType”源码:
@override
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
return ancestor;
}
其实就是像上边说的那样仅仅获取到InheritedWidget对象没有其他的任何操作。所以只有获取对象的作用。
看一下“dependOnInheritedWidgetOfExactType”源码:
@override
T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
可以看出它也是是先通过“_inheritedWidgets”来获取到InheritedWidget对象,但和“getElementForInheritedWidgetOfExactType”方法不同的是接着又调用“dependOnInheritedElement(ancestor, aspect: aspect) as T”方法。来看一下它的源码:
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
内部会调用InheritedElement的updateDependencies方法,将当前的elment传入进去。
@protected
void updateDependencies(Element dependent, Object aspect) {
setDependencies(dependent, null);
}
该方法的内部接着会调用setDependencies方法将当前的Element接着传递下去。
@protected
void setDependencies(Element dependent, Object value) {
_dependents[dependent] = value;
}
而setDependencies方法的内部就是将当前的elment通过_dependents已HashMap的形式保存下来。
final Map<Element, Object> _dependents = HashMap<Element, Object>();
InheritedWidget更新数据
先来看一下更新的时候InheritedWidget会执行“updated”方法 @override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
我们注意到在调用父类的updated方法之前需要进行调用“updateShouldNotify”方法进行判断。这个方法是InheritedWidget的抽象方法,需要它的子类来实现。而这个方法也同样控制了是否会进行数据的同步操作,这样通过操作就可以由子类来控制了。
接下来来看一下调用了父类ProxyElement的updated方法都干了什么?
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
@protected
void notifyClients(covariant ProxyWidget oldWidget);
父类的updated方法会调用“notifyClients”这个抽象方法,具体的内容需要由子类来实现,那么我们来看InheritedElement的notifyClients这个方法吧。
@override
void notifyClients(InheritedWidget oldWidget) {
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (final Element dependent in _dependents.keys) {
assert(() {
// check that it really is our descendant
Element ancestor = dependent._parent;
while (ancestor != this && ancestor != null)
ancestor = ancestor._parent;
return ancestor == this;
}());
// check that it really depends on us
assert(dependent._dependencies.contains(this));
notifyDependent(oldWidget, dependent);
}
}
它会通过“_dependencies”当前的InheritedElement对象并调用“notifyDependent”方法。
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
而它内部就会调用了Element的“didChangeDependencies”的方法。这也就是为什么子组件在依赖了InheritedWidget数据的话,子组件的didChangeDependencies方法会被调用,而在它的方法里边会调用markNeedsBuild重新build。到此为止InheritedWidget的源码分析已经结束了。