<link rel="stylesheet" href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/kdoc_html_views-1a98987dfd.css">
<link rel="stylesheet" href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/ck_htmledit_views-25cebea3f9.css">
<div id="content_views" class="htmledit_views">
<h1><a name="t0"></a>1、framework源码组成</h1>
Flutter中widget、state、element的源码位于framework.dart中,整个文件6693行(版本Flutter 3.12.0-14.0.pre.28)。整个代码可划分为若干部分,主要包括key、widget、state、element四部分。
1.1 key
关于key的代码65行到272行,这里的key包括ObjectKey、GlobalKey、LabeledGlobalKey、GlobalObjectKey。整个key体系的代码还包括key.dart这个文件,里面包括Key、LocalKey、UniqueKey和ValueKey。Key是GlobalKey和LocalKey的抽象基类。LabeledGlobalKey和GlobalObjectKey是GlobalKey的抽象子类。ObjectKey、UniqueKey和ValueKey是LocalKey的三个具体子类。
1.2 widget
关于widget的代码274行到1922行。包括10个抽象类:Widget、StatelessWidget、StatefulWidget、ProxyWidget、ParentDataWidget、InheritedWidget、RenderObjectWidget、LeafRenderObjectWidget、SingleChildRenderObjectWidget、MultiChildRenderObjectWidget。这些类,大体可以把Widget分为组合式、渲染、功能性三种。
1.3 state
State的代码在823附近。State是一个抽象类。State首先起着一个枢纽的作用,它持有widget,也持有element。从state里获取context,只是简单返回持有的element。另一方面,State对外提供了widget的生命周期:initState、didUpdateWidget、reassemble、deactivate、activate、dispose、didChangeDependencies。这些生命周期方法是系统提供给我们的钩子。如果我们要主动发起渲染请求的话,就要调用State提供给我们的setState方法。而build则是我们告诉系统如何渲染这个widget的地方。前者提供时机,后者提供内容。
1.4 BuildContext
BuildContext是一个抽象类,代码位于2129-2485行。Element实现了BuildContext。
1.5 BuildOwner
BuildOwner位于2511-3168行。
1.6 element
element相关的代码位于3259行到6597行之间。接近一半的代码量,可以看出element是核心部分。
2 StatelessWidget和StatefulWidget
Widget是一个抽象类,里面关键的三个东西:
-
final Key? key;
-
-
Element createElement();
-
-
static
bool canUpdate(Widget oldWidget, Widget newWidget) {
-
return oldWidget.runtimeType == newWidget.runtimeType
-
&& oldWidget.key == newWidget.key;
-
}
我们要关注下canUpdate这个方法的实现,决定我们能否复用这个widget是由这个widget的runtimeType和key决定的。runtimeType表明了widget的类型,不同类型的widget是不能复用的。key是我们人为指定的一个值,它可以在同类型widget之间产生个体差异,以便我们或者渲染系统找到它。不设置的时候就是null,这时候只比较runtimeType就行。
我们先来介绍widget中的组合式子类:StatelessWidget和StatefulWidget。StatelessWidget创建的element是StatelessElement:
-
@override
-
StatelessElement createElement() => StatelessElement(
this);
而StatefulWidget创建的是StatefulElement,并且还能创建state:
-
@override
-
StatefulElement createElement() => StatefulElement(
this);
-
-
@protected
-
@factory
-
State createState();
StatelessWidget和StatefulWidget仍然是抽象类,需要我们子类化。根据源码,我们发现StatefulWidget和StatelessWidget只负责创建对应element,并不持有它。而Statefulwidget只负责创建state,同样也并不持有它。
3 RenderObjectWidget
RenderObjectWidget有三个抽象子类LeafRenderObjectWidget、SingleChildRenderObjectWidget、MultiChildRenderObjectWidget。分别代表了没有孩子、只有一个孩子和有多个孩子的三种RenderObjectWidget。源码我们不再展开,想必你也猜到了是一个啥都没,一个有个child,最后一个有个children属性罢了。
相比于前面的StatelessWidget,RenderObjectWidget返回自己独特的RenderObjectElement,并且还多了一个RenderObject:
-
RenderObject createRenderObject(BuildContext context);
-
-
-
@protected
-
void updateRenderObject(BuildContext context,
covariant RenderObject renderObject) { }
-
-
-
@protected
-
void didUnmountRenderObject(
covariant RenderObject renderObject) { }
RenderObjectWidget我们后面在RenderObjectElement的performRebuild里讲到了,它会调用updateRenderObject进行更新。这里我们无法展开讲updateRenderObject,需要去看具体子类里的实现。
3.1 ColoredBox
我们以SingleChildRenderObjectWidget的一个具体子类ColoredBox为例:
-
final Color color;
-
-
@override
-
RenderObject createRenderObject(BuildContext context) {
-
return _RenderColoredBox(color: color);
-
}
-
-
@override
-
void updateRenderObject(BuildContext context, RenderObject renderObject) {
-
(renderObject
as _RenderColoredBox).color = color;
-
}
我们发现它创建的RenderObject是一个私有类_RenderColoredBox。而我们前面提到的updateRenderObject在这里只是设置一下新的颜色。
我们再转去看_RenderColoredBox的实现:
-
class _RenderColoredBox extends RenderProxyBoxWithHitTestBehavior {
-
_RenderColoredBox({
required Color color })
-
: _color = color,
-
super(behavior: HitTestBehavior.opaque);
-
-
/// The fill color for this render object.
-
///
-
/// This parameter must not be null.
-
Color
get color => _color;
-
Color _color;
-
set color(Color value) {
-
if (value == _color) {
-
return;
-
}
-
_color = value;
-
markNeedsPaint();
-
}
-
-
@override
-
void paint(PaintingContext context, Offset offset) {
-
// It's tempting to want to optimize out this `drawRect()` call if the
-
// color is transparent (alpha==0), but doing so would be incorrect. See
-
// https://github.com/flutter/flutter/pull/72526#issuecomment-749185938 for
-
// a good description of why.
-
if (size > Size.zero) {
-
context.canvas.drawRect(offset & size, Paint()..color = color);
-
}
-
if (child !=
null) {
-
context.paintChild(child!, offset);
-
}
-
}
-
}
主要是根据颜色在canvas上绘制背景色和child。设置新颜色会引起markNeedsPaint。markNeedsPaint相关代码在RenderObject里。
3.2 markNeedsPaint
-
void markNeedsPaint() {
-
-
if (_needsPaint) {
-
return;
-
}
-
_needsPaint =
true;
-
// If this was not previously a repaint boundary it will not have
-
// a layer we can paint from.
-
if (isRepaintBoundary && _wasRepaintBoundary) {
-
-
if (owner !=
null) {
-
owner!._nodesNeedingPaint.add(
this);
-
owner!.requestVisualUpdate();
-
}
-
}
else
if (parent
is RenderObject) {
-
parent!.markNeedsPaint();
-
}
else {
-
-
// If we are the root of the render tree and not a repaint boundary
-
// then we have to paint ourselves, since nobody else can paint us.
-
// We don't add ourselves to _nodesNeedingPaint in this case,
-
// because the root is always told to paint regardless.
-
//
-
// Trees rooted at a RenderView do not go through this
-
// code path because RenderViews are repaint boundaries.
-
if (owner !=
null) {
-
owner!.requestVisualUpdate();
-
}
-
}
-
}
这里出现了新的Owner:PipelineOwner。markNeedsPaint标记自己需要重新绘制,如果自己是绘制边界,就把自己加入需要绘制的节点列表里。如果不是绘制边界,就调用父节点的markNeedsPaint。这里只是简单标记和放入列表,真正执行绘制的时机是在WidgetsBinding.drawFrame里的flushPaint:
-
void drawFrame() {
-
buildOwner!.buildScope(renderViewElement!);
// 1.重新构建widget
-
super.drawFrame();
-
//下面几个是在super.drawFrame()执行的
-
pipelineOwner.flushLayout();
// 2.更新布局
-
pipelineOwner.flushCompositingBits();
//3.更新“层合成”信息
-
pipelineOwner.flushPaint();
// 4.重绘
-
if (sendFramesToEngine) {
-
renderView.compositeFrame();
// 5. 上屏,将绘制出的bit数据发送给GPU
-
}
-
}
3.3 markNeedsLayout
上面的代码里,我们也看到了布局是在这之前的flushLayout执行的。RenderBox源码里PipelineOwner通过markNeedsLayout标记、收集需要布局节点:
-
void markNeedsLayout() {
-
-
if (_needsLayout) {
-
-
return;
-
}
-
if (_relayoutBoundary ==
null) {
-
_needsLayout =
true;
-
if (parent !=
null) {
-
// _relayoutBoundary is cleaned by an ancestor in RenderObject.layout.
-
// Conservatively mark everything dirty until it reaches the closest
-
// known relayout boundary.
-
markParentNeedsLayout();
-
}
-
return;
-
}
-
if (_relayoutBoundary !=
this) {
-
markParentNeedsLayout();
-
}
else {
-
_needsLayout =
true;
-
if (owner !=
null) {
-
owner!._nodesNeedingLayout.add(
this);
-
owner!.requestVisualUpdate();
-
}
-
}
-
}
-
-
void markParentNeedsLayout() {
-
assert(_debugCanPerformMutations);
-
_needsLayout =
true;
-
assert(
this.parent !=
null);
-
final RenderObject parent =
this.parent!;
-
if (!_doingThisLayoutWithCallback) {
-
parent.markNeedsLayout();
-
}
else {
-
assert(parent._debugDoingThisLayout);
-
}
-
assert(parent ==
this.parent);
-
}
我们发现布局标记和绘制标记的实现是类似的,都需要标记自身,都需要向上寻找布局或者绘制的边界。PipelineOwner最终对其调用performLayout和markNeedsPaint:
-
void flushLayout() {
-
-
try {
-
while (_nodesNeedingLayout.isNotEmpty) {
-
final
List<RenderObject> dirtyNodes = _nodesNeedingLayout;
-
_nodesNeedingLayout = <RenderObject>[];
-
dirtyNodes.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
-
for (
int i =
0; i < dirtyNodes.length; i++) {
-
if (_shouldMergeDirtyNodes) {
-
_shouldMergeDirtyNodes =
false;
-
if (_nodesNeedingLayout.isNotEmpty) {
-
_nodesNeedingLayout.addAll(dirtyNodes.getRange(i, dirtyNodes.length));
-
break;
-
}
-
}
-
final RenderObject node = dirtyNodes[i];
-
if (node._needsLayout && node.owner ==
this) {
-
node._layoutWithoutResize();
-
}
-
}
-
// No need to merge dirty nodes generated from processing the last
-
// relayout boundary back.
-
_shouldMergeDirtyNodes =
false;
-
}
-
-
-
for (
final PipelineOwner child
in _children) {
-
child.flushLayout();
-
}
-
-
-
}
finally {
-
_shouldMergeDirtyNodes =
false;
-
-
}
-
}
-
-
-
-
void _layoutWithoutResize() {
-
-
RenderObject? debugPreviousActiveLayout;
-
try {
-
performLayout();
-
markNeedsSemanticsUpdate();
-
}
catch (e, stack) {
-
_reportException(
'performLayout', e, stack);
-
}
-
_needsLayout =
false;
-
markNeedsPaint();
-
}
performLayout这个方法在RenderBox里实现为空,需要子类自行实现。
前面ColoredBox这个例子里我们在updateRenderObject里改变颜色并不会引起布局变化。现在我们找一个RenderPositionedBox的源码来看看。
3.3 RenderPositionedBox
RenderPositionedBox是Align使用的renderObject。我们看看它的updateRenderObject实现:
-
void updateRenderObject(BuildContext context, RenderPositionedBox renderObject) {
-
renderObject
-
..alignment = alignment
-
..widthFactor = widthFactor
-
..heightFactor = heightFactor
-
..textDirection = Directionality.maybeOf(context);
-
}
再进到RenderPositionedBox的set alignment实现:
-
set alignment(AlignmentGeometry value) {
-
if (_alignment == value) {
-
return;
-
}
-
_alignment = value;
-
_markNeedResolution();
-
}
-
-
void _markNeedResolution() {
-
_resolvedAlignment =
null;
-
markNeedsLayout();
-
}
我们发现设置新的alignment,会引起markNeedsLayout的调用。
4 三个功能性widget:ProxyWidget、ParentDataWidget、InheritedWidget
暂不展开
5 StatefulElement和StatelessElement
我们再跳到StatelessElement构造方法:
-
-
class StatelessElement extends ComponentElement {
-
/// Creates an element that uses the given widget as its configuration.
-
StatelessElement(StatelessWidget
super.widget);
-
-
@override
-
Widget build() => (widget
as StatelessWidget).build(
this);
-
}
StatelessElement自身可以通过build返回子element对应的widget。
而StatefulElement构造方法:
-
/// Creates an element that uses the given widget as its configuration.
-
StatefulElement(StatefulWidget widget)
-
: _state = widget.createState(),
-
super(widget) {
-
state._element =
this;
-
state._widget = widget;
-
}
我们发现在创建element的时候,会先调用widget的createState创建state,并指向它,然后state就伸出两只手,一只手拉着widget,另一只手拉着element。element里面有一个重要的方法:
Widget build() => state.build(this);
这里我们可以认为state build出来的是element持有的widget的“child”。事实上,无论StatelessElement还是Statefulwidget,它们都没child这个概念,但是对应的element是有一个child的属性的。所以我们姑且这么看待它们的关系。这里把element传进去,只是因为我们可能需要用到element树一些上下文信息。
3.1 setState
现在看看我们的老朋友,state里的setState方法的实现:
-
void setState(VoidCallback fn) {
-
_element!.markNeedsBuild();
-
}
-
-
-
void markNeedsBuild() {
-
if (dirty) {
-
return;
-
}
-
_dirty =
true;
-
owner!.scheduleBuildFor(
this);
-
}
-
-
void scheduleBuildFor(
Element element) {
-
_dirtyElements.add(element);
-
element._inDirtyList =
true;
-
}
-
-
void rebuild() {
-
performRebuild();
-
}
-
-
void performRebuild() {
-
_dirty =
false;
-
}
完整的流程如下:
中间省略一些代码,我们直接跳到performRebuild实现。对于基类Element的实现,只是简单标记为dirty。Element分为渲染和组件两种类型,前者与渲染相关,后者用于组成其他element。
3.2 performRebuild
对于跟渲染相关的RenderObjectElement的performRebuild,则需要更新它的renderObject:
-
void _performRebuild() {
-
(widget
as RenderObjectWidget).updateRenderObject(
this, renderObject);
-
super.performRebuild();
// clears the "dirty" flag
-
}
对于跟组件相关的ComponentElement的performRebuild实现:
-
void performRebuild() {
-
Widget? built;
-
try {
-
built = build();
-
}
catch (e, stack) {
-
-
}
finally {
-
// We delay marking the element as clean until after calling build() so
-
// that attempts to markNeedsBuild() during build() will be ignored.
-
super.performRebuild();
// clears the "dirty" flag
-
}
-
try {
-
_child = updateChild(_child, built, slot);
-
}
catch (e, stack) {
-
_child = updateChild(
null, built, slot);
-
}
-
}
这里的核心是会调用build方法创建新的widget,然后使用这个widget去更新child element。从前面的代码中我们可以看到statefulElement和statelessElement的build实现是有差异的。但是返回的widget,其实都是它们的child对应的widget。在多个渲染周期,child element会一直存在,而需要更新时widget就会重新创建。更新后的Element设置为当前Element的child。至于怎么更新,我们等下再讲。
ComponentElement是ProxyElement、StatefulElement和StatelessElement的父类。但是只有StatefulElement覆写了performRebuild。进一步来到StatefulElement的performRebuild实现:
-
void performRebuild() {
-
if (_didChangeDependencies) {
-
state.didChangeDependencies();
-
_didChangeDependencies =
false;
-
}
-
super.performRebuild();
-
}
StatefulElement增加的任务是如果依赖发生了变化,要触发state的didChangeDependencies方法。
3.3 updateChild
回到前文,我们再来看Element的updateChild实现:
-
Element? updateChild(
Element? child, Widget? newWidget,
Object? newSlot) {
-
// 如果'newWidget'为null,而'child'不为null,那么我们删除'child',返回null。
-
if (newWidget ==
null) {
-
if (child !=
null) {
-
deactivateChild(child);
-
}
-
return
null;
-
}
-
-
final
Element newChild;
-
if (child !=
null) {
-
// 两个widget相同,位置不同更新位置。先更新位置,然后返回child。这里比较的是hashCode
-
if (child.widget == newWidget) {
-
if (child.slot != newSlot) {
-
updateSlotForChild(child, newSlot);
-
}
-
newChild = child;
-
}
else
if (Widget.canUpdate(child.widget, newWidget)) {
-
//两个widget不同,但是可以复用。位置不同则先更新位置。然后用新widget更新element
-
if (child.slot != newSlot) {
-
updateSlotForChild(child, newSlot);
-
}
-
child.update(newWidget);
-
newChild = child;
-
}
else {
-
// 如果无法更新复用,那么删除原来的child,然后创建一个新的Element并返回。
-
deactivateChild(child);
-
newChild = inflateWidget(newWidget, newSlot);
-
}
-
}
else {
-
// 如果是初次创建,那么创建一个新的Element并返回。
-
newChild = inflateWidget(newWidget, newSlot);
-
}
-
-
return newChild;
-
}
这里关键的两个方法是update和inflateWidget。
3.4 update
对于不同类型的child的update方法是不一样的。基类Element只是用新的替换旧的而已:
-
void update(
covariant Widget newWidget) {
-
_widget = newWidget;
-
}
对于StatelessElement的update,就是直接更换widget:
-
@override
-
void update(StatelessWidget newWidget) {
-
//直接更换widget
-
super.update(newWidget);
-
assert(widget == newWidget);
-
rebuild(force:
true);
-
}
对于StatefulElement的update,除了更换widget,还要更换state指向的widget:
-
void update(StatefulWidget newWidget) {
-
super.update(newWidget);
-
-
final StatefulWidget oldWidget = state._widget!;
-
state._widget = widget
as StatefulWidget;
-
final
Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget)
as
dynamic;
-
rebuild(force:
true);
-
}
最后都通过调用rebuild,标记自身dirty。
对于SingleChildRenderObjectElement就是对它的child调用updateChild,对于MultiChildRenderObjectElement就是对它的children调用updateChildren:
-
void update(SingleChildRenderObjectWidget newWidget) {
-
super.update(newWidget);
-
_child = updateChild(_child, (widget
as SingleChildRenderObjectWidget).child,
null);
-
}
-
-
void update(MultiChildRenderObjectWidget newWidget) {
-
super.update(newWidget);
-
final MultiChildRenderObjectWidget multiChildRenderObjectWidget = widget
as MultiChildRenderObjectWidget;
-
_children = updateChildren(_children, multiChildRenderObjectWidget.children, forgottenChildren: _forgottenChildren);
-
_forgottenChildren.clear();
-
}
而对于ProxyElement主要是更新widget和通知:
-
@override
-
void update(ProxyWidget newWidget) {
-
final ProxyWidget oldWidget = widget
as ProxyWidget;
-
//使用新的widget更新持有的widget
-
super.update(newWidget);
-
//通知其他关联widget自己发生了变化
-
updated(oldWidget);
-
//标记dirty
-
rebuild(force:
true);
-
}
-
-
@protected
-
void updated(
covariant ProxyWidget oldWidget) {
-
notifyClients(oldWidget);
-
}
3.5 updateChildren
updateChild前面我们已经提到了,而对于updateChildren的实现:
-
List<
Element> updateChildren(
List<
Element> oldChildren,
List<Widget> newWidgets, {
Set<
Element>? forgottenChildren,
List<
Object?>? slots }) {
-
-
Element? replaceWithNullIfForgotten(
Element child) {
-
return forgottenChildren !=
null && forgottenChildren.contains(child) ?
null : child;
-
}
-
-
Object? slotFor(
int newChildIndex,
Element? previousChild) {
-
return slots !=
null
-
? slots[newChildIndex]
-
: IndexedSlot<
Element?>(newChildIndex, previousChild);
-
}
-
-
// This attempts to diff the new child list (newWidgets) with
-
// the old child list (oldChildren), and produce a new list of elements to
-
// be the new list of child elements of this element. The called of this
-
// method is expected to update this render object accordingly.
-
-
// The cases it tries to optimize for are:
-
// - the old list is empty
-
// - the lists are identical
-
// - there is an insertion or removal of one or more widgets in
-
// only one place in the list
-
// If a widget with a key is in both lists, it will be synced.
-
// Widgets without keys might be synced but there is no guarantee.
-
-
// The general approach is to sync the entire new list backwards, as follows:
-
// 1. Walk the lists from the top, syncing nodes, until you no longer have
-
// matching nodes.
-
// 2. Walk the lists from the bottom, without syncing nodes, until you no
-
// longer have matching nodes. We'll sync these nodes at the end. We
-
// don't sync them now because we want to sync all the nodes in order
-
// from beginning to end.
-
// At this point we narrowed the old and new lists to the point
-
// where the nodes no longer match.
-
// 3. Walk the narrowed part of the old list to get the list of
-
// keys and sync null with non-keyed items.
-
// 4. Walk the narrowed part of the new list forwards:
-
// * Sync non-keyed items with null
-
// * Sync keyed items with the source if it exists, else with null.
-
// 5. Walk the bottom of the list again, syncing the nodes.
-
// 6. Sync null with any items in the list of keys that are still
-
// mounted.
-
-
int newChildrenTop =
0;
-
int oldChildrenTop =
0;
-
int newChildrenBottom = newWidgets.length -
1;
-
int oldChildrenBottom = oldChildren.length -
1;
-
-
final
List<
Element> newChildren =
List<
Element>.filled(newWidgets.length, _NullElement.instance);
-
-
Element? previousChild;
-
-
// 从前往后依次对比,相同的更新Element,记录位置,直到不相等时跳出循环。.
-
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
-
final
Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
-
final Widget newWidget = newWidgets[newChildrenTop];
-
-
// 注意这里的canUpdate,本例中在没有添加key时返回true。
-
// 因此直接执行updateChild,本循环结束返回newChildren。后面因条件不满足都在不执行。
-
// 一旦添加key,这里返回false,不同之处就此开始。
-
if (oldChild ==
null || !Widget.canUpdate(oldChild.widget, newWidget)) {
-
break;
-
}
-
final
Element newChild = updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!;
-
-
newChildren[newChildrenTop] = newChild;
-
previousChild = newChild;
-
newChildrenTop +=
1;
-
oldChildrenTop +=
1;
-
}
-
-
// 从后往前依次对比,记录位置,直到不相等时跳出循环。
-
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
-
final
Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenBottom]);
-
final Widget newWidget = newWidgets[newChildrenBottom];
-
-
-
if (oldChild ==
null || !Widget.canUpdate(oldChild.widget, newWidget)) {
-
break;
-
}
-
oldChildrenBottom -=
1;
-
newChildrenBottom -=
1;
-
}
-
-
// 至此,就可以得到新旧List中不同Weiget的范围。
-
final
bool haveOldChildren = oldChildrenTop <= oldChildrenBottom;
-
Map<Key,
Element>? oldKeyedChildren;
-
// 如果存在中间范围,扫描旧children,获取所有的key与Element保存至oldKeyedChildren。
-
if (haveOldChildren) {
-
oldKeyedChildren = <Key,
Element>{};
-
while (oldChildrenTop <= oldChildrenBottom) {
-
final
Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
-
-
-
if (oldChild !=
null) {
-
if (oldChild.widget.key !=
null) {
-
oldKeyedChildren[oldChild.widget.key!] = oldChild;
-
}
else {
-
deactivateChild(oldChild);
-
}
-
}
-
oldChildrenTop +=
1;
-
}
-
}
-
-
// 更新中间不同的部分,如果新旧key相同就更新一下重新利用,否则新的widget就没有旧的对应,是插入行为
-
while (newChildrenTop <= newChildrenBottom) {
-
Element? oldChild;
-
final Widget newWidget = newWidgets[newChildrenTop];
-
if (haveOldChildren) {
-
final Key? key = newWidget.key;
-
if (key !=
null) {
-
// key不为null,通过key获取对应的旧Element
-
oldChild = oldKeyedChildren![key];
-
if (oldChild !=
null) {
-
if (Widget.canUpdate(oldChild.widget, newWidget)) {
-
// we found a match!
-
// remove it from oldKeyedChildren so we don't unsync it later
-
oldKeyedChildren.remove(key);
-
}
else {
-
// Not a match, let's pretend we didn't see it for now.
-
oldChild =
null;
-
}
-
}
-
}
-
}
-
-
final
Element newChild = updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!;
-
-
newChildren[newChildrenTop] = newChild;
-
previousChild = newChild;
-
newChildrenTop +=
1;
-
}
-
-
// We've scanned the whole list.
-
// 重置
-
-
newChildrenBottom = newWidgets.length -
1;
-
oldChildrenBottom = oldChildren.length -
1;
-
-
// 将后面相同的Element更新后添加到newChildren,至此形成新的完整的children。
-
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
-
final
Element oldChild = oldChildren[oldChildrenTop];
-
-
-
final Widget newWidget = newWidgets[newChildrenTop];
-
-
-
final
Element newChild = updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!;
-
-
-
newChildren[newChildrenTop] = newChild;
-
previousChild = newChild;
-
newChildrenTop +=
1;
-
oldChildrenTop +=
1;
-
}
-
-
// 清除旧列表中多余的带key的Element
-
if (haveOldChildren && oldKeyedChildren!.isNotEmpty) {
-
for (
final
Element oldChild
in oldKeyedChildren.values) {
-
if (forgottenChildren ==
null || !forgottenChildren.contains(oldChild)) {
-
deactivateChild(oldChild);
-
}
-
}
-
}
-
-
return newChildren;
-
}
dif算法相对比较复杂,可能理解起来比较困难。值得一提的是,无论 updateChild还是updateChildren都实现在基类element里。同层diff算法里使用key并不是出于性能考虑,没有key能够就地复用,使用key能够指定复用对象。有时候就地复用会有一些问题,譬如某个widget自身有一些状态,你如果就地复用其他widget,就会导致这些状态的丢失。
3.6 inflateWidget
再来看看inflateWidget的实现,它主要是用来创建新的element,并且mount。如果widget有GlobalKey的话,则会尝试获取对应的element,然后更新后返回。
-
Element inflateWidget(Widget newWidget,
Object? newSlot) {
-
-
try {
-
//如果widget带key,并且是GlobalKey,则尝试获取一下对应的element,并用新的widget更新它然后返回
-
final Key? key = newWidget.key;
-
if (key
is GlobalKey) {
-
final
Element? newChild = _retakeInactiveElement(key, newWidget);
-
if (newChild !=
null) {
-
-
newChild._activateWithParent(
this, newSlot);
-
final
Element? updatedChild = updateChild(newChild, newWidget, newSlot);
-
-
return updatedChild!;
-
}
-
}
-
-
// 这里就调用到了createElement,重新创建了Element
-
final
Element newChild = newWidget.createElement();
-
-
newChild.mount(
this, newSlot);
-
-
return newChild;
-
}
-
}
3.7 mount
我们再来看看element基类的mount:
-
void mount(
Element? parent,
Object? newSlot) {
-
-
_parent = parent;
-
_slot = newSlot;
-
_lifecycleState = _ElementLifecycle.active;
-
_depth = _parent !=
null ? _parent!.depth +
1 :
1;
-
if (parent !=
null) {
-
// Only assign ownership if the parent is non-null. If parent is null
-
// (the root node), the owner should have already been assigned.
-
// See RootRenderObjectElement.assignOwner().
-
_owner = parent.owner;
-
}
-
-
final Key? key = widget.key;
-
if (key
is GlobalKey) {
-
owner!._registerGlobalKey(key,
this);
-
}
-
_updateInheritance();
-
attachNotificationTree();
-
}
mount就是将自身插入父element的某个slot中。我们发现Element在mount的时候,会将父element的ower设置给自己。如果widget带有key,那么ower会将这个element注册到自己的map里。
而对于组合式Element的mount有所差异,除了上述基类行为,还会调用_firstBuild:
-
@override
-
void mount(
Element? parent,
Object? newSlot) {
-
super.mount(parent, newSlot);
-
_firstBuild();
-
-
}
-
-
void _firstBuild() {
-
// StatefulElement overrides this to also call state.didChangeDependencies.
-
rebuild();
// This eventually calls performRebuild.
-
}
对于StatelessElement,_firstBuild的实现只是单纯rebuild一下。而对于StatefulElement:
-
@override
-
void _firstBuild() {
-
-
final
Object? debugCheckForReturnedFuture = state.initState()
as
dynamic;
-
state.didChangeDependencies();
-
super._firstBuild();
-
}
我们发现_firstBuild里调用了state的initState方法,这里说明我们在state里实现的生命周期方法,其实会被StatefulElement根据自身的不同状态而调用。因此其他方法我们不再赘述。
3.8 why?
在参考文章里有一个问题,我们来分析一下,增加我们对本文的理解程度。现在我们有如下一段代码:
-
import
'dart:math';
-
-
import
'package:flutter/foundation.dart';
-
import
'package:flutter/material.dart';
-
-
void main() => runApp(MyApp());
-
-
class MyApp extends StatelessWidget {
-
@override
-
Widget build(BuildContext context) {
-
return MaterialApp(
-
title:
'Flutter Demo',
-
theme: ThemeData(
-
primarySwatch: Colors.blue,
-
),
-
home: MyHomePage(title:
'Home Page'),
-
);
-
}
-
}
-
-
class MyHomePage extends StatefulWidget {
-
MyHomePage({Key key,
this.title}) :
super(key: key);
-
-
final
String title;
-
-
@override
-
_MyHomePageState createState() => _MyHomePageState();
-
}
-
-
class _MyHomePageState extends State<MyHomePage> {
-
List<Widget> widgets;
-
-
@override
-
void initState() {
-
super.initState();
-
widgets = [
-
StatelessColorfulTile(),
-
StatelessColorfulTile()
-
];
-
}
-
-
@override
-
Widget build(BuildContext context) {
-
return Scaffold(
-
appBar: AppBar(
-
title: Text(widget.title),
-
),
-
body: Row(
-
children: widgets,
-
),
-
floatingActionButton: FloatingActionButton(
-
child: Icon(Icons.refresh),
-
onPressed: _swapTile,
-
),
-
);
-
}
-
-
_swapTile() {
-
setState(() {
-
widgets.insert(
1, widgets.removeAt(
0));
-
});
-
}
-
}
-
-
class StatelessColorfulTile extends StatelessWidget {
-
-
final Color _color = Utils.randomColor();
-
-
@override
-
Widget build(BuildContext context) {
-
return Container(
-
height:
150,
-
width:
150,
-
color: _color,
-
);
-
}
-
}
-
-
class Utils {
-
static Color randomColor() {
-
var red = Random.secure().nextInt(
255);
-
var greed = Random.secure().nextInt(
255);
-
var blue = Random.secure().nextInt(
255);
-
return Color.fromARGB(
255, red, greed, blue);
-
}
-
}
代码可以直接复制到DartPad中运行查看效果。 或者点击这里直接运行。
效果很简单,就是两个彩色方块,点击右下角的按钮后交换两个方块的位置。上面的方块是StatelessWidget
,那我们把它换成StatefulWidget
呢?。
-
class StatefulColorfulTile extends StatefulWidget {
-
StatefulColorfulTile({Key key}) :
super(key: key);
-
-
@override
-
StatefulColorfulTileState createState() => StatefulColorfulTileState();
-
}
-
-
class StatefulColorfulTileState extends State<StatefulColorfulTile> {
-
final Color _color = Utils.randomColor();
-
-
@override
-
Widget build(BuildContext context) {
-
return Container(
-
height:
150,
-
width:
150,
-
color: _color,
-
);
-
}
-
}
再次执行代码,发现方块没有“交换”。这是为什么?结论是widget层面而言,两个widget的确发生了交换,但是Element并没有发生交换,原来位置的Element持有的state build出原来颜色的Container。
6 key
可以看参考,这里暂不展开
7 BuildOwner
buildOwner是framework这些代码背后的大boss。我们来看看它做了哪些事情。每个element都指向一个Owner用来维护它的生命周期:
-
BuildOwner?
get owner => _owner;
-
BuildOwner? _owner;
为什么我们能用globalKey找到对应的element,没有什么神奇的,因为buildOwner有一个map维护着globalKey和element的对应关系:
-
final
Map<GlobalKey,
Element> _globalKeyRegistry = <GlobalKey,
Element>{};
-
-
void _registerGlobalKey(GlobalKey key,
Element element)
-
void _unregisterGlobalKey(GlobalKey key,
Element element)
buildOwner另一个作用是维护着element的build列表:
-
final
List<
Element> _dirtyElements = <
Element>[];
-
-
void scheduleBuildFor(
Element element) {
-
if (element._inDirtyList) {
-
_dirtyElementsNeedsResorting =
true;
-
return;
-
}
-
if (!_scheduledFlushDirtyElements && onBuildScheduled !=
null) {
-
_scheduledFlushDirtyElements =
true;
-
onBuildScheduled!();
-
}
-
_dirtyElements.add(element);
-
element._inDirtyList =
true;
-
-
}
WidgetsBinding会通过WidgetsBinding.drawFrame调用buildOwner的buildScope:
-
void drawFrame() {
-
buildOwner!.buildScope(renderViewElement!);
// 1.重新构建widget
-
super.drawFrame();
-
//下面几个是在super.drawFrame()执行的
-
pipelineOwner.flushLayout();
// 2.更新布局
-
pipelineOwner.flushCompositingBits();
//3.更新“层合成”信息
-
pipelineOwner.flushPaint();
// 4.重绘
-
if (sendFramesToEngine) {
-
renderView.compositeFrame();
// 5. 上屏,将绘制出的bit数据发送给GPU
-
}
-
}
buildScope对_dirtyElements里的element调用rebuild:
-
void buildScope(
Element context, [ VoidCallback? callback ]) {
-
if (callback ==
null && _dirtyElements.isEmpty) {
-
return;
-
}
-
try {
-
_scheduledFlushDirtyElements =
true;
-
if (callback !=
null) {
-
_dirtyElementsNeedsResorting =
false;
-
try {
-
callback();
-
}
-
-
}
-
_dirtyElements.sort(
Element._sort);
-
_dirtyElementsNeedsResorting =
false;
-
int dirtyCount = _dirtyElements.length;
-
int index =
0;
-
while (index < dirtyCount) {
-
final
Element element = _dirtyElements[index];
-
try {
-
element.rebuild();
-
}
-
-
index +=
1;
-
if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting!) {
-
_dirtyElements.sort(
Element._sort);
-
_dirtyElementsNeedsResorting =
false;
-
dirtyCount = _dirtyElements.length;
-
while (index >
0 && _dirtyElements[index -
1].dirty) {
-
// It is possible for previously dirty but inactive widgets to move right in the list.
-
// We therefore have to move the index left in the list to account for this.
-
// We don't know how many could have moved. However, we do know that the only possible
-
// change to the list is that nodes that were previously to the left of the index have
-
// now moved to be to the right of the right-most cleaned node, and we do know that
-
// all the clean nodes were to the left of the index. So we move the index left
-
// until just after the right-most clean node.
-
index -=
1;
-
}
-
}
-
}
-
-
}
finally {
-
for (
final
Element element
in _dirtyElements) {
-
assert(element._inDirtyList);
-
element._inDirtyList =
false;
-
}
-
_dirtyElements.clear();
-
_scheduledFlushDirtyElements =
false;
-
_dirtyElementsNeedsResorting =
null;
-
}
-
}
后面的流程就回到了我们前面的performRebuild方法 。
8 没有覆盖的内容
本文没有提及具体的布局逻辑,将在后面的文章里进行讲述。
9 图示
文中出现的一些关键类的继承关系:
参考
1.说说Flutter中最熟悉的陌生人 —— Key_flutter globalkey 源码_唯鹿的博客-CSDN博客