开篇

上一篇 从源码看flutter(一):Widget篇 咱们了解到了关于 Widget 的相关常识, 知道了 Element 都是经过 WidgetcreateElement() 办法来创立的。

那么,是谁] B U : ^调用了 createElement() 办法?经过查找, 发现只要两处调用了这个办法。分别是:

  • Elem- | v Y & mentinflateWidget(...) 办法
  • RenderObjectToWidgetAdapterattachToRenderT0 F P / Bree(...) 办法

第一个办法在 Eles E z ? Lment 内部,而且不是 static 办法,明显 Element 不或许随便调用自己的办法创立自己, 所以它是用来生成其他 Element 目标的。而第L F X I ( J一个 Element 便是在第二个办法被创立出来的。

在咱们介绍 Element 目标之前,咱们能够先简略了解一下第一个 Element 的创立过程

RenderObjectToWidgetElement

咱们知道,flb + r g J 4 G {utter的进口在 runApp(widget) 办法里,咱们能够看一下:

runApp(app)

void runApp(Widget app) {
WidgetsFlutterBinding., X f D f v 9 D 1ensureIni( F | ? e }tiali3 M f  Z W i m azed()
..scheduleAttachRootWidge+ & n -t(app)
..schedule4 S l t ! _ T *Warmn * ] v ( / y 5 .UpFrame(Y } 3);
}

在这儿进行了一切的初始化操作,而咱们经过 runApp(app6 | e v . Q p Y t) 传入的根 Widget 被第二个Z [ $ & p ,办法 scheduleAtta; U c lchRX a j . c [ l oootWidget(app) 所调用,从这个办法进入

scheduX X EleAttachRootWidW A e [ 8 Uget= r 6 X 8(app)

mixin WidgetsBinding8 0 ~ # ! ( j E on BindingBase,~ x i 3 D { u * ServicesBinding, SchedulerBinding, GesturM Q 0 GeBinding, RendererBiD + S Y ( h # K Xnding, SemanticsBinding {
...
@protectr _ ~ I J X (ed
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
...
void attachRootWidget(Widget rootWie ? D 8 t T udget) {
_renderV( b * BiewElementd S y s v = RenderObjectToWG * %idgetAdapter<RenderBox>(
container: renderView,
debugShot 3 = H z h a ?rtDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderF o 8 kViewElement as Ren0 ? M L O } ]derObjectToWidgetElement<RenderBox>);
}
...} ? H P ` k S Z 8
}

能够看到,最终经过创立 RenderObjectToWidgetAdapter 目标,并调用其 attachToRenderTree(...) 办法创立了 RenderObjectToWidgetElemM w $ Nent,咱们简略了解一下

attac8 J 8 ] 4 $ 5hToRenderTrK p X Z L 5 * 4ee(…)

class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
...
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwne) C ( @ Nr owner, [ RenderObjectToW` F b : jidgetElement<T> element ]) {
...
eles V # K b ? g ument = createElement(n j . ! I Z S);
...
return element;
}
...
}

这儿的 createElement() 也便是咱们之前提到过的,第二个调用的当地。之后5 B r & 8 n T T一切的 Element 都是经过其 父Element 调用 inflateWidget(...) 办法所创立了

接下来,咱们开端正式介绍 Element 目标

Element

咱们常用的 Statef h # g G Y ^fulWidgetStatele@ Z L + A JssWidgeW - u % - c & zt 所对应的 Element 目标,承继联系如下:

xxxElement -> ComponentElement -> Element

许多其他的 Element 目标也都是直接或许间接承继于 ComponentElement ,不过 RenderObjectWidgn : / } } p w r oeB 4 T 1tElement 承继联系如下:

RenderObjectElement -> Element

下面,咱们从 Element 的结构函数开端

Element(widget)

  Elemz [ y L #ent(Widget widget)
: assert(1 E K N 3 g | : owidget != null),
_widget = widget;

在结构函数里边,进行了 Element 所对应的 Widget 目标的赋值。接下来看一看 Element 的结构

abstra. H W j - X a 0 (ct class Element extends DiagnosticableTree implements BuildContext {
...
}

Di| z ^ 9 w kagnosticableTree 在第一篇现已介绍过,这儿不再赘述。能够看到这儿有个咱们了解的目标 BuildContext , 常常能够在 WidgetStatebuild(...)t Y A W 3 U s 8法中看到它,咱们先来简略的了o , Z { E d ]解一下它

BuildContext

abstract class B~ ? { 3 ^ Z ) h *uildContext {
Widget get2 ~ 0 widget;
BuildOwner get owner;
...
RenderObject findRenderObject();
.Y @ N ..
InheritedElemen ) o n r S U xt getElementf ? x 1 ? cForInheritedWidgetOfExactType<T extends InheritedWidget>();
T findAncestorWidgetOfExactType<T extends Widget>();
...
void visitChildElements(ElementVisitor visitor);
...
}

上面列出了比较典型的一些办法。BuildContext 是一个抽象类,由于 dart 中没有 Interface ,而这儿的 BuildContext 本质上只提供各种调用办法,所以完全能e B ? / 0 O a r够把它当成 java 中的接口

其间 BuildOwner 目标只在 WidgetsBiJ L 7 f d tndinginitInstances() 中初始化过一次,也便是说大局只要唯一的实例。他是 widget framework 的管理类,实际上的的作用有许多,比方在 Element 中,就负责管理它的生命周期

其他的一些办法:

  • findRenderObject(): 用于回来当时 Widget 对应的 RenderObp b w 9 3 W jject ,假如当时 Widget 不是 RenderObjectWidget 则从children中寻找
  • getElementForInheritedWidgetOfExact` lType(): 在保护的 Map<Type, InheritedElement> 中查找 InheritedElement,在咱们熟知h J : } w F ?Provider 中的 Provider.o* 4 N (f<T>(context) 便是经过这种办法获取数据类的
  • findAncestorWidgetOfExactType(): 经过遍历 ElS Q . $ -ementparent 来找到指定( ; 8 i类型Widget
  • visitChildElements(): 用于遍历 子Element

BuildContext ; ~ 大致就介绍这些8 g @ ; o T w,接下来咱们来看 Element 中的一些成员变量

Element的成员变a _ – U

abstract class Element extends DiagnosticableTree implements BuildContext {
...
Element _par p f E R - +rent;
...
dynamic _slot;
...
int _depth;
...
Widget _widget;
...
BuildOv B X n D 7 jwner _owner;H ) 4 x %
...
bool _active = false;
...
_ElementL_ s H w * t K _ifecycle _debugLifecycleState = _ElementLifecycle.initial;
...
Map<Type, InheritedElement> _inheritedWidgets;
...
bool _dirty = true;
...
bool _inDirtyList = false;
}

上面列举出了首6 Z f * w S x I要的一些成员变量

Element 中默许持有^ x [ ( p k parent 目标,而 slot 用于表明它在 parentchild列表 的方位,假如 parent 只要一个 ch8 ~ r & j i +ildslot 应该为 nA i [ull ,再来看看剩余的一些变量

  • depth : 当时 ElemeM z N [ B Snt 节点在树中的深度,深度是递加的,且有必要大于0
  • _active: 默许为 false, 当 Element 被增加到树2 R Y F p – x 1 (后,变为 true
  • _inheritedWidgets: 从 parent 一直传递下来,保护了一切e + f C { M 7 InheritedElement ,不过很猎奇为什么这儿不直接用 stax # , Ctic 修饰,是为了方便垃圾回收吗?
  • dirty: 假如为 true 就表明需求 reBu( ) u 1ild 了, 在 markNeedsBuild() 中会被设为 true
  • _inDC E U ZirtyList: 当 Element 被标记为 dirty 后,随之会将 ElemT q g eent 放入 BuildOwner 中的 _dirtyElements ,并设置为F [ J ^ n d x 1 p true ,等待 reBuild

还有一个 生命周期目标 _debugLifecycleState

enum _E( # 7lementLifecyX ] } ~ H H m xcle {
initial,
active,
inactive,
defunct,
}

它对外部是隐藏的,这个生命周期和 State 的有点相似,不过其间的 activeinactive 是能够来回切换的,这儿就涉及到 Eleme& } @ 0 } V @ jnt 的复y / ! I l = e用了,后边会说

然后是 Element 的一些首要办法,& [ ^ – T ;咱们简略的看一下

Element的办法

  RenderObr V 4 ) n M |ject get renderObject) e 4 $ x z { ... }
voi} G } Z l ? 9d visiw  qtChk o 4ildren(ElementVisitor visitor) { }
@override
void visitChildElements(ElementVisitor visitor) {
...
visitChildren(visitor)K : L _ Y n Y G B;
}
@protected
Element: G Q d b T # R Q updateChild(Element child, Widget newWidget, dynamic newSlot) {9 $ C l V f i ... }
@mustCallSuper
voidK W w @ Y mount(Element parent, dynamic newSlot) { ... }
@mustCallSup( C c 4er
void update(covariant Widgetv 3 . : 7 _ ( f M newWidget) {
...
_widget = newWidget;
}
Element _retakeInactiveElement(GlobalKey key, Widget newWidgeZ B y It7 K v U w Y) { ... }
@protected
Element inf; p X E | @ f 6 )lateWidget(Widge~ A 5 ~ v kt newWidget, dynamic newSlot) { ... }
@protected
vL w # q e $ qoid deactivateChild(Eleme(  int child) { ... }
@mustCallSuper
vo; g ] # . t ~ vid activate() { ... }
@mustCallSuper
void deacti* ; Z G F d { ] mvate() { ... }
@mustCallSuper
void unmount() { ... }
@mustCallSuper
void didChangeDependencies() {
...
markNeedsBuiln 9 + R Qd();
}
void markNeedsBuild(){ ... }
void rebuild() { ... }
@protected
void performRebuild();

上面的首要办法中,最中心的是 mouH # | @nt()unmount()inflo T ^ v L }ateWidget(...)updatu S t eChild(...)rebuild() 这些M w 4 R H { C I

这儿咱V K 3 ! O h 5们不去直接介绍这些办? @ s } s p法的作用,由于脱离上下文独自看的话或许阅$ Z –览体验不会太好,后边会走一遍 Element 的创立流程,在这个T S j S ` Z B z B过程中去阐述各个办法的作用。

不过咱们能够先看其间一个办法 rE P U ` w G 5 nenderObject 了解一下 ElementRenderObject 的对应联系

  RenderObject get renderObject {
RenderObject result;
void visit(Element element) {
assert(result == null); // this verifies that there's only oc ~ ~ + v 3ne child
if (element is RenderObjectG +  +Element)
result = el| Y a W  v - 6 Rement.renderObject;
else
element.visitChildren(visit);
}
visit(this);
return result;
}

解释一下便是,假如当时 elementRenderObjectElemenw n , j g n Mt 的话,直接回来它持有的 renderObject ,否则遍历 children 去获取最近的 renderObject 目标

从这儿也能够知道 RenderObject 只与 Rend* s * T . LerObjectElement 是一一对应的,与其他 Element 则是一对多的联系,也验证了咱们上一篇中的判定

不过这儿有一点需求吐槽的是,在办法里边直接界说办法,阅览体验不是特别好,而后边这样的状况还会许多

接下来,咱们预备进入 Element 的创立流程进口

Element 创立流程进口

既然要走创立流程,自然是要找个起点的。在上一篇中,咱~ : T们知道} . v s f经过 createElement() 创立 Element 的办法只在两个当地被调用:

  • 其一是作为根节点 Elf } ; * O O { dementRenderObjec3 f F + h F h mtToWidgetElementRenderObjectToWidgetAdapterattachToRenderTree(...) 中被创立
  • 另一个是其他一切 ElementinflateWidget(...) 办法中被创立

咱们以第二个办法为进口,进入 Element 的创立流程,先简略的看一下第二个办法

abstract cD z ! v ( o 1 | |lass Element extends Diagnostica l y { 1 v d GbleTree imZ a & f h z +plements BuildContext {
...
@protected
Element inflateWidget(Widget newWidget, dynamic newSlot) {
.Z j U ~ N 1 o ; 0..
final Elemeq j Int newCd M ( g q r u } (hild = newWidg*    t j Tet.createElement~ Q - 0 K * Y();v @ Q v
...
newChild.mount(this, newSlot);
assert(newChild._debugLifP V D T % AecycleState == _ElementLi; 9 xfecycle.active);
return newChild;
}
...
}

能够看到,上面最终调用了 Elementmount(...) 办法,所以这个办法算是各个 Element 的进口了。

上一篇咱们提到过,不同 Widgeta - Y 对应 Element 的完成都不相同,其间最广泛的两种完成分别是 ComponentElementRenderObjectElement

咱们能够从第一个开端了解

Comp# K T ? m D ponentc e bElement 的创立流程

进入它的 moV a i y 1 Gunt(...) 办法

mount(…)

  void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
...
_firstBuild();
...
}

调用了父类,也便是 Elementmount(...)

Element -&gt] 4 z ! : _ n ; mount(…)

  @mustCallSuper
void mount(Element parent, dynamic newSlot) {
...
_parent = parent;; i Y G i a V }
_slot = newSlot;
_depth = _parents g 2 U { V != null ?7 1 X . j _parent.depth +F a 7 =  t 1 : 1;
_active = true;
if (parent != null) // OnlyZ J : H j x ) assign owne*  ! h g 4 Jrship if the parent is non-null
_owner = parent.owneQ T u B | m E  Cr;
final Key key = widget.key;
if (key is GlobalKey) {
key._registerg H R w b $ v *(this);
}
_updateInheritance();
assert(() {
_debugLifecyn ! D . D 0 ccleState = _ElementLifecycle.8 G T w 5 q m N ;act4 : r 9 Cive;
return true;
}());
}

能够看到,在 mount(...) 中,进行了一些列的初始化操作。

其间假如传入的 keyGlobalKey ,会将@ E ] L q ~ ) m D当时 Element 存入 GlobalKey 中保护的 Map<G s 5 s R w d %GlobalKey, Element> 目标。

最终会将生命周期设置为 _ElementLif8 O @ r ~ b L ~ecycle.active

接下来,能够看一下 ComponentElement_firstBuild()

_firstBuo T lild()

  void _firstBuild() {
rebuild()Y # :;
}

调用了 rebuild() ,它是在 Element 中完成的

Element -> rebuild()

  void rebuildr P E f , q 4 z s() {
...
performRebuilg l  * 2 6 p Rd();
...
}
@protected
voiK 6 b N 7 Od performRebuio 2 =ld();

最终调用到 performRebuilQ e l d K |d() 办法,Element 中这个办法什么都没做,便是交由子2 b D x B类去完成的,接下来回到 ComponentElement

performRebuild()

  @override
voi@ S 7d perfoT P ~rmRebuild() {
if (!kReleaseMode &+ $ @ Q 1 |amp;& debugProfileBuildsEnaq } X } + o a i ybled)
Timeline.startSync('${widget.runtimeType}',  arguments: timelineWhitelistArguments);
...
Widget built;
try {
built = build();
...
} catch (e, stack) {
built = ErrorWidget.builder(...);
} fin+ m pally {
...
_dirty = false;
...
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);Q : & F P , | q m
} catch (e, stack) {
built = ErrorWidget.builder(...);
_child = updD b y I 7 } 4 ateChild(null, built, slot);
}
if (!kReleaseMode &L 1 t X ) s 9amp;& debugProfileBuildsEnabled)
Timel1 u s @ P | % -ine.finish y  } e ~ ( 9Sync();j % c  f b p
}

能够看到,开头和结尾都做了debug形式的判别,并使用了 Timeline 这个目标,它的作用其实便是咱们之前介绍过的,用于在 DevTool 中检测性能体现

能够看到,上面经过调用咱们最了解的 build() 办法来创立 Widget ,假如发作异常的话,就( j n会在 catc_ 3 h a 7 bh 语句中创立一个 ErrorWidq . { # fget, 也便w * }是咱们常常遇见的那个红色的错误界面啦!

后边会经过 updateF V K xChild(...) 来给当时 Element_child 赋值

updateChild(...) 位于 Element

Element -> updateChildS 9 – 2 x W(…)

  Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
...
if (newWidget == null) {
if (child != null)
deactivateChild(cT O ? S K ghild);
retub j 9 N Z wrn null;
}
if (child != null) {
if (child.widget == newWidget) {
if (child.si v T z V ( L 2lot != newSlot)
updateSlotForChild(child, newSlg + O o | q ! O 3ot);
return child;
}
if (Widn  tget.canUpdate(child.widget, newWidget)) {
if (c~ @ Uhild.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
asse2 , lrt(child# y { ? i.widget == newWidget);
assert(() {
child.owner._debugElemen; h ? c f 1 [tWasRebuilt(child);
return true;
}());
return child;
}
deac% 7 n m itivateChild(child);
assert(child._parent == nu) N u j b p Mll);
}
return infl] F ateWidget(newWi2 K M odget, newSlot);
}

updateChild(...) 是非常重要的一个办法,它接受三个参数,分别是 Element childWidget newWidget 以及 dynamic newSlot,传入的参数不同,这个办法的作用也不相同,首要分为下面几种状况:

newWidget为null newWidget不为null
child为null ①.s M | B 5 9 3 I回来null ②.回来新的Elment
child不为null ③.移除传入child,回来null ④.依据 canUpdate(…) 决定回来更新后的child或许新Element

其间的 deactivateChild(child) 便是将传入 Element 移除去

而咱们k H 4 9 & o KperformRebuild() 中创来的值是: child为null 而且 newWidget不为null,属于第二种状. K = u况。= ^ @ U 0 K直接进入 inflateWidget(...) 办法

Element -> inf+ c D G X [lateWidget(…)

又回到最初的起点

  @protected
Element inflateWidget(WidgeF B # v % S dt newWidget, dynamic newSlot) {
assert(newWidget != null);
final KL X qey key = newb 9 ?  a v & YWidget.key;
if (key is GlobalKey) {
final Element newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
assert(newChild._parent == null);
assert(() {
_debuU : 9 q A O T 1gCheckForCycles(newChild);
return true;
}());
newChild._acc 7 StivateWith` j J 4 / % h 8 EParent(A B 4this, newSlot);
final Element updU  j 2 1atedChild = updateChl o ` 3ildM ? & 4(newChild, newWidget, newSlot);
assert(newChiO Y o eld == updatedChild);
return updatedChild;
}
}
//创立新的Element,开端下一轮循环
return newChild;
}

办法后部分的逻辑之前现已说过,这儿能够看一下前部分关于 GlobalKey 的部分。假如获取到 widgetkeyGlobal! Q g b t / GKey, 而且之前 Widget 现已在 Elementmount(...) 中注册到了 GlobalKey的话,就会在这儿取出而且复用。这部分是在 _retv ) T { ` . b u HakeInactiveElement(...) 完成的,能够简略看一下5 Q 4 k | a % e P

  Elemei [ Q @ % W ; {nt _retakeInactiveElement(GlobalKey key, Widget newWidg- p z  Xet) {
final Element element = key._currentElement;
if (j Y 1 D f 1 E _ Welemen6 : Y =t == null)w . V
return null;
if (!Widget.canUpdate(element.widget, newWidget))
return null;
...
return element;
}

Element不存在或许无法更新时,则不会进行复用,回来 null

假如结果不为null,后边{ 8 ) 2 X 5 n再调用 updateChild7 p ? r Q ((...) 办法,这儿传入的参数都不为null– C v 4 ) ) $ A .,所以会进入之前所说的第四种状况:

  Elx g ! =ement updateChild(Element child, Widget newWidget, dynamic newSlot) {
...
//这儿一定是trueifx Q X ` s ~ s (Wid! s n o e ` C t 2get.canUpdate(child.^ # G 9 bwidget, newWidget)) {
if (child.slot != n5 % 6 a A + n 3ewSlot)
updateSU 2 M f ZlotForChild(child, newSlot);
child.update(newWidget);
assert(ch% - P Kild.widget == newWidget);
assert(() {
child.owner._debugElemen% $ r btWasRebuilt(child);
return true;
}());
return child;
}
..M F U.
return inflateWidget(newWidget, newSI 4 d ( X v %lot);
}

注意到上面的部分,调用 chi$ - )ldupdate(newWidget) 办法,这个办法除了更新当时 Element 持有的 Widget 外,剩余的逻辑都交给子类去完成了

那么 ComponentElement 的创立流程大致就讲到这儿

下面,咱们能够看一下 ComponentElement 的两个子类^ B ^ StatelessElementStatefulElement

StatelessElement

class StatelessElement exten@ i p b ) = hds ComponentElemv r ! m k ; h Hent {
/// Creates_ E R d & G ? an elu l 0 Kement that uses the given widget asZ 5 b its configurX * q Y ! :ation.
StatelessElement(StatelessWidget widget) : super(widget);
@override
StatelessWidget get widG p @ b ) 6 T cget =>3 D = 3; sup. a *er.widget as Statel3 K } 0 , TessWidgeE e q u A 3 U x vt;
@override
Widget build() => widget.build(this);
@override
void upB X Q K 7 ; cdate(StatelessWidget newWidget) {
supi , 4 ;er.update(newWidget);
assert(widget == newWidget);
_dirty = true;
rebuild();
}
}

StatelessE@ a T % 1 i %lement 非常简略,重写的 updv ) q . 2 { 5 v ;ate(...) 也仅仅调用了 rebuild(),感觉没有什么可说的。

接下来看看 Su y X ` i d 8tatefulElement

StatefulElement

class StatefulElement extends ComponentElement {
StatefulEl* D o g 7ement(StatefulWidC h Kget widget)
: _statz * e = widget.createSta! #  K 8te(),
super(widget) {
...
_state._el: + # ( $ ; ` Gement = this;
...
_state._widget = widget;
...
}
@override
void _firstBuild() { ... }
@override
void update(State: { ? 1fulWidget newWidget) { ... }
@override
void unmount()_ 5 + : 3 l { ... }
}

这儿展示了 StatefulElement 一些首要的办法,能够看r 8 h到在结构函数中把 ElementWidget^ w Y Y A ! 目标放入了 State

接下来看一下剩余三个办法都做了什么

_fir[ U V n 5 C a YstBuild()

  @override
void _firstBuild() {
...
final dynamic debugCheckForReturnedFuture = _state.inib @ Y j d  StState() as dynamic;
...
_state._debugLifecy) + YcleState = _StateLifecycle.initialized;
...
_state._ - z ~ YdidChangeDependencies();
...
_state._debugLifeW ^ ` :cyc* + h Q 8 h I v yleState = _StateLij d -fecycle.ready;
.3 U Y . 6 h..M 1 O u L p
super._firstBuild();
}

ComponentElement 中是在 mount(...)里调用 _firstBv # + Juild() 的,这儿重写了这个办法,而且在里边进行了一些初始化的操作,而且调用了 StateinitState() 办法

update(…)

  @override
void update(Z / M [ _ _ EStatefulWidget newWidget) {
super.u# 6 rpdate(newWidget);
...
final StatefulWidget oldWi9 5 u # d o [ udget = _state._widget;
...
_dirty = true;
_state._widget = widget as StatefulWidget;
...
final dynamic debugCheckForReturnedFutud O P . J z W h cre = _state.didUpdateWidget(oldWidget) as dynamic;
...
rebuild();
}

在这儿首要是调用了 StatedidUpdateWidget(..r ] X D ) M 8.) 办法,其他内容和 StatelessElement 差不多

那么到了这儿,关于 Statef] U xulWidget中咱们常用的 setState() 办法| = & f V Y w ` d,它具体会走过 StatelessElement 的哪些过程呢,下面咱们就来看一下

StatefulElement的改写流程

咱们知道 StatesetState() 办法会调用 ElementmarkNeedsBuild()

Element -> markNeedsBuild()

  BuildOwner get owC # u G I Y Fner => _owner;
void markNeedsBuild() {
...
if (d4 t l ~ h ) mirty)
return;
_dirty = true;
owner.scheduleBuildFor(thi) 7 ms);
}

下面进入 BuildOwner 看看 schedu: k F nleBuildFor(element) 做了些什么

BuildOwner -> scheduleBuildFor(element)

  final List<Element>R ; 2 V P; _dirtyElements = &l) = & S I d : !t;Element>[];
void scheduleBuildFor(Element element) {
...
if (element._inDirtyList) {
...
_dirtyElementsNeedsResorting = true;
return;
}
...
if (!_scheduledFlushDirtyElements &&: ) T * x Xamp;}  [ 8 z 6 { onBuildScheduled != null) {
_scheduledF2 e ) E + IlushDirtyElements = true;
onBuildScheduled();
}
_dirtyElements= 2 - U | ; ^.add(element);
element._inDirtyList = true;
...
}

能够看到,scheduleBuildFor(element) 后边w v = ^ / $ ; T会将需求改写的 Element 增加到 _di& J C | R d e h 0rtyElements 中,并将该 Element_inDirtyList 标记为 t U 9rue

但之后W & n P并没( d c *有做其他的操作,那改写到底是如何进行的呢?这就要看前面调用的一个办法 onBuildScheduled()

BuildOwner -> onBuildScheduled()

这个办法是在 BuildOwner 被创立时设置的

mixin WidgetsBinding on BindingBase, ServiceH A Y L a Q 4 V 8sBinding, SchedulerBib _ ynding, GestureBinding, RendererBindint = c % } - bg, SemanticsBinding {
...
@o( Q E - 9verride
voio } 1 ` Y + P | [d initInstances() {
...
_buildOwner = BI V KuildOwner();
buildOwner.onBuildScheduled = _handleBuildSco T g Cheduled;
...
}
...
}

来看一下 _handleBuildScheduled

Widge= I z b N | –tsBinding -> _g s 6 ) G 6handleBuildScs : q Iheduled

  void _handleBuildScheduled() {
...
ensureVisualUpdate();
}

ensureVisualUpdate()SchedulerBinding 中被界说

mixin SchedulerBinding on BindingBase, ServicesBinding {
...
void ensureVisualUpdate() {
switch (schedulerPhase) {
...
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
...
}
}
...
}

后边会进入 scheduleFrame() 办法

SchedulerBinding -> scheN N V u [duleFrame()

  @proteb N Rcted
void ensureFrameCallb/ c _ b v 0 e ]acksRegistered() {
window3 8 m  o A %.o% ~ j o ynBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDe | M J )rawFrame;
}
void scheduleFrame() {
...
ensureFrameCallbacksRegistered();
window.scheduleFrame();
_hasScheduledFrame ={ f R Q true;
}

这儿会调用 window.scheduleFrame()

Window –n Y 1> scheduleFramez A E()

class Window {
...
/// Requests that, at the next appropriate opportunity, the [onBeginFrame]
/// and [onDrawFrame] callbacks be invoked.T / V - [ k s ^
///
/// See also:
///
///  * [S : ; l 0chedulerBinding], the Fluttew ; y ? = & ( 3 `r framework class which manages thex i h r S
///    scheduling of frames.
void scheduleFrame() native 'Window_scheduleFrame';
}

到了这儿,便是对 engine 的相关操作了。其间,经过各式各样的操作之后G ] W y _ ^ f a,会回调到 dart 层的 _drawFrame()F ; – b l

sky_engine -> ui -> hooks.dart -> _drawFrame()

假如你对 engine 中的这一部分操作感兴趣的话,能够看一下这一篇文章 Fd T 7 * p 3 Tlutter渲染机制—UI线程, 由于都是C++的内容,逾越了我的才能领域,所以这儿直接去看大神的吧

_drawFrame() 办法内容如下:

void _drawFrame() {
_invoke(window.onDrawFrame, window._onDrawFra9 G c b { , p meZol b b 1 g dne);
}

最终,会调用到之前在 SchedulerBindingonDrawFrame 所注册的 _I Q G , ) {handleDrawFrame 办法, 它会调用 handleDrawFrame()

SchedulerBinding ->x J q . i v handleDrawFra3 ^ ome()

  void handleDrawFrame() {
...
try {
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPha1 Y q p G Jse.persistentCallbacks;
for (FrameCallback callback in _persistentCallbacks)
_i5 ~ v ` n W b n ynvokeFrameCallback(callbaN F Kck, _currentl M / & GFrameTimeStamp);
...
}
...
}

在这y : _ _儿会遍历 _persistentCas P N n K .llbacks 来履行对应办G % ; 5 t a e Q &法,它是经过 RendererBindingaddPersistentFrameCallback 增加,而且之后的每一次 framg F – n 4 . I ye 回调都会遍历履行一次

这儿即将履行的办法,是在 RendererBindinginitInstances() 中增加的 _handleS * S kPersistentFrameCallback

  void _hanB s s f 0 0 KdlePersistentFrameCallback(Durationa W S x | n  2 ) timeStamp) {
drawFrame();
_mouseTrackew 8 | S = 3 m yr.schedulePostFrameCheck();
}

最终,会调用到 WidgetBindingdrawFrame()

WidgetBinding -> drawFrame()

  @override
void drawFrame() {
...
try {
if (renderViewElement != null)
bx [ } 7 T z ! v duildOwner.buildScope(renderViewElemW V + D . , ] : dent);
super.drawFrN 5 -am` C I +e();
buildOwner.finalizeTree();
}
...
}

renderV* N $ Y @ :iewElement 便是咱们之前看 runApp() 流程中所创立的 根Element

最终调= ! r h s又回到了 BuildOwner 目标,并调用它的 buildScope(...) 办法

BuildOwner -> buildScope(…)

buildScope(...) 用于对 Element Tree 进行部分更新

  void buildScope(Element context, [ VoidCallback callback ]) {
...
_debugBuilding = true;
...
_scheduledFlushDirtyEleme, D H Y 5 ^ Onts = true;
...
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyEleme] V 9 b v s znts.lengtho % L : X A;
int index = 0;
while (index < dD w + 1irtyCount) {
...
_dirtyElements[index].rebuild();
...f 0 ) c S F _ 4 )
index += 1;
...
//假如在@ $ d _ @ L `部分更新的过程中,_dirtyElements发作了变化
//比方8 V w y U或许有新的目标插入了_dirtyElements,就在这儿进f k *行处理
}
...
for (Element element in _dirtyElements) {
assert(element._inDirtyList);
element._inDirtyList = false;
}
_dirtyElementsa % r n q x g N.cle2 ; F M v [ar();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
...
_debugB* n :  wuildinw v g = false;
...
}

所以其实从上面咱们就能知道,部分更新的原理便是把需求更新的目标W P } s I存入了 _dirtyElementU w c r = ns 中,然后在需求更新的时分,遍历它们,进行 reBuild()

遍历之前,会调用 sort(...) 办法k 2 6进行排序,判别条件是 ElX 0 ( Q x P B f 1ement 的深度,依照从小到大排列,也便是关于 Element 是从上往下更新的。

更新完毕后,_dirtyElements 会被清空,各个标志位也会被重置

到这儿,StatefulElement 的改写流程咱们现已了解到了,接下来咱们了解一下它的毁掉流程,一起趁便也能x n Z G T , p hZ ^ @ ^知道 Element 的毁掉流程

StatefulElement的毁掉流程

其实在 drawFrame()buildScope(...)i P V 9 _法后紧跟着的 buildOwner.finalizeTree() 便是用于进行G * = Q ] @ , $ + Element 的毁掉的

不过这儿不将它作为进口,记住咱们之前 ElementupdateChild(...) 办法里边,& N L q 9 $ V有两处会对 Element 进行毁掉,3 K X而调用毁掉的办法便是 deactivateChild(child)

下面就从这儿作为进口

Element -> deactivateChild(child)

  @protected
void deactivateChilZ 5 x ! Bd(Element child) {
...
child._parent = null;
child.detachRenderObject();
owner._inactiveElements@ 7 a v q 8 [ X 3.add(child);
...
}

在这儿会清除去 child 关于 parentF j ~ r D 0 . 的引证,一起也调用 detachRend A q X 3 derObject() 去毁掉 RenderObject,关于它的细节下一片蘸6 p @ = e再说。

最首要的? v P p M – & J `还是向 BuildOwner_inactiveElements 中增加了当时要毁掉的 child

咱们能够先了解一下 _inactiveElements

_InactiveElements

class _I # 2nactiveElements {
bool _locked = false;
final Set&` 0 9lt;Ey I 1lement> _elements = H) e h n ~ @ . _ashSet<Element>Q j Q();
static void _deactivateRecursively(Element el+ ? a . t kement)) g A + D {
...
element.deactivate();
...
element.visitChildren(_deactivateRecursively);
...
}
void add(Element element) {
...
if (element._active)
_deactivateRecursively(element);
_elements.add(d R  W T 6 v 6 3element);
}
}

能够看到,_InactiveE# ` / o 4 Wlements 中使用 Set 存放了一切需求毁掉的目标

add(element) 办法中,假如当时要毁掉的目标还处于活跃状况,则经过递归的办法,遍历它的 children ,对每个 child Element 调用 deactivate() 来设为毁掉状况,能够看一下 deactivate() 办法

  ///Element
@mustCallSuE S T i / ) J q {per
void deacti& 9 N | - zvate() {
...
_inhe, j p & i a @ritedWidgets = null;
_active = false;
.5 ] u ] b..
_debugL l .ifecyA E b TcleState = _ElementLifecycle.inact= Z J fiv~ | ; n ; Dep = u;
...
}

即将被毁掉的 Element 生命周期会变为 inactive

这儿咱们收集完要毁掉的 Element 之后呢,就会在Widget6 K e O 6 SsBindingdrawFrab A ] . me()H i V – F 9 k触发时调用0 3 % 0 n a t g g finalizeTree() 来进行真实的毁J l 2 g s * 9掉了

BuildOwner -> finalizeTree()

  void finalizeTrea S *e() {t ) a p
...
lockState(() {
_inactiveElements._un* P n ]mountAll(); // this unregisters the GlobalKeys
});
...
}

这儿调用了 _Inactivs h b 4 B ^ 6eElements_unmountAll() 来进行毁掉

_InactiveElements -> _unmountAll()

  voidS ) D - - y E 3 6 _unmountAll() {
_lockeT - ~ ) a 3 D i _d = true;
final List<Element> elements = _elements.toList()..sv 5 ~ x U c +ort(Element._sort^ G p ] e 1 C $ d);
_elements.cleari P g();
try {Q p ! S N 1 S ]
elements.reX ^ o % & C / o qversed.forEach(_unmount);
} finally {
asseh 9 X nrt(_elements.isEmpty);
_locked = false;
}
}

这儿的毁掉也是由上到下的,调用了 _unmount(element) 办法

  void _unmount(Element el. p D c f s # bement) {
...
element.visitChildren((Element child) {
assert(child._parent == element);
_unmount(child);
});
element.unmount();P 7 c p V k
...
}

不得不说,dart 将办法作为参数传递,而且在某些状况下能够省掉输入办法的参数真是好用,不过或许就牺牲了一点 ] f x可读性

_unmount(element) 也是遍历 children ,然后调用 child Elementunmount() 来进行毁掉

_InactiveElements 中还有一个 remove 办法,介绍 unmount() 前咱们先把这个给看了

_InactiveElements -> remove()

  voidF J / ~ H remove(Element element) {
...
_elements.remove(element);
...
}

这儿便是从 Set4 ; } 中移除传入的 Element 目标,在之前的 _unmountAll() 中会经 u 3 W pclear() 清楚 SeX 2 ` | Ct 内一切的元素,那为什么这儿会有这样一种4 x s状况呢?之前咱们在 ElementinflateWidget(...) 中提到过,GlobalKey 是能够用于复用 Element 的,被复用的% 3 $ Element 目标无需重新创立。咱们再来看一下

Element -> _retakeInactiveElement(.– , H d ? 7 l U..)

  @protected
EM B h Q c T 5 H Jlement ins P 5 ) k J { z wfla- y U V [ N n ~teWidget(Widg^ 8 Q I Fet newWidget, dynamic newSlot) {
...
final Key key = newWidget.key;
if (key is GlobalKey) {
final Element newChild = _retake& D a T Z , b O !InactiveElement(keyZ G d y * G L j b, newWidget);
if (newChildi - L != null) {9 8 8 ( V 1 G 6 h
...
n- e S M l DewChild._activateWithPH P Karz , l 4 e n xent(this, newSlot);
final Element updatedChild = updateChild(newChild, newWidget, newSlot)q ! 2 . ] !;
asseP j / M ! a -rt(newChild == updatedChild);
return updatedChild;
}
}
...
}
Element _retakeInactiva & C d L 5eElemC N Z uent(GlobalKey key, Widg#  )et newWidget) {
...
final Elh f ] Gement element! z ] m d # v 6 = key._currentElement;
if (element == null)
return null;
if (!W7 Z Y Zidget.canUpdate(elemenQ 6 t 8 D ] ct.wV t widget, newWidget))
retI | g + 9 @ , F |urn null;
...
final Element parent = eT ? $lement._parent;
if (parent !V . r { Q F a= null) {
...
parent.forgetChild(element);
parent.deactivateChild(element);
}
...
owner._inactiveElements.remove(elemen r x Q K v z n Hnt);
return element;
}6 n #

能够看到,在 _retakeInactiveE( ~ 1 C } ; ` Llement(...) 末尾处,会将被复用了的 Element_inactiveElements 中移除去。获取到这个 E( N G a Plement 后,会调{ p ~ P Z , V –_activateWithParent(...) 办法再8 6 6 Y s T次将 Element 激活

Element -> _activateWithParent(…)

  void _activateWithParent(Element parent, dynu V /  Samic newSlot) {
...
_parent = parent;
...
_updateDepth(_parent.depth);
_activateRecursiM | | 1 & w p z mvel g 7 [y(this);
attachRR W , | Y K 6 R SenderObject(newSlot);
...
}
static void _activateRecursively(Element element) {
...
elW ` Y d h _ wement.al ! o - k 3ctivate();
...
element.visitC3 - 4hildren(_activateRecursively);
}

这儿会经过递归调用,激活 Element 和它的 children ,来看一下 active() 办法

Element -> activate()

  @mustCallSuper
void activatL Z j A v He() {
...
final bool hadDependencies = (_dependencies != null && _dependenZ $ {cies.isNotEmpty) || _hadUnsatisfiedDependencies;
_active = true;
...K C 3
_depeM ! _ w a =ndencies?.[ & q . a Eclear();
_hadUnsatisfiedDependencies = falsG | F L O *e;
_updateInherit# G dance();
...
_debugLifecycleStatc I 5 ; _ 7 ! 3 Te = _ElementLifecycle.active;
...
if (_dirty)
oy { . Bwner.scheduleBuildFor(this);
iW + S } J Zf (hadDependencies)
didChangeDependencies();
}
///St- p V ]atefulElement
@override
void ac0 G Q S @ Ctivate() {
super.activate();
...
markNeedsBuild();
}

acb a Q *tivate() 中,Element 的生命周期再次变为了 active, 这也便是咱们之前所说过的, Element 的四个生命周期中, U W f 4 @ 7或许会呈现 activeinactive? j J [ n / 切换的状况。

那么在哪种状况下会触发这种复用呢?其实很简略:W ` ; : k 2 1 } G处于不同深度的同一类型 Widget 使用了同一个 GlobalKey 就能够, 比方下面这样:

Center2 + ? + D U z(
child: changed
? Container(
child: Text('aaa! } 9 , M x ) Wa', key: globalKey),
padding: EdgeInsets.all(20))
: Text('bbb'c 3  !, key: globalKey),
)

当经过 setState 修改 changed 时,就能3 I –够触发复用

插曲就说到这儿,咱0 b F , m 0 ! J G们继续之前的 unmount() 办法

Element -> unmount()

  @mustCallSuper
void unmount() {
...
final Key key = widget.key;
ia H S ^ % h $ Yf (key is GlobalKey) {
key._unregister(this);
}& w , ;
assert(() {
_debugLifecycleState = _ElementLifecycle.defunct;
retur7 , M 2n true;
}());
}

能够看到. O 3 6ue w h T r M 7 Y Xnmount() 中,假如当时C 4 f 4 o 0 M ] Element 注册了 Globalkey 就会被清空掉,一起生命周期会被设置为 defu@ y Rnct,在 StatefulElement 中会重写这个办法

StatefulElement -> unmount()

  @override
void unmount() {
super.unmount();
_state.dispose();
...
_state._element = null;
_state = null;
}

在这儿调用了 Statedispose() 办法,而且在之后清理了 State 中持有的 Element 引证,最终将 State 置空

到了这儿,StatefulElement 的毁掉流程也完毕了,本篇文章也接近尾声

当然,还有 RenderObjectE) j 5 g 8 h m _lement 都还没有做过剖析,由于一切和 Renderb / iObject 有关的内容都将放到第三篇来讲,这儿就先越过吧

总结

  • Element 是真实的数据持有者,而 State 也是在它的结构函数里被创立,它的生命周期比 State 略长。
  • 每次改写时,Widget 都会被; 2 X K , a N r重新创立,而在 Element 的创立流程完毕后, Element 只要在 canUpdate(...) 回来 false 时才会重新创立,不然一般都是调用它的 update(...) 进行更新。StatelessElement 也是这样的。
  • GlobalKey 除了能够跨 Widgeth r . m M ; .递数据外,还能够对 Element 进行复用

剩余的总结,就看图片吧

注:以上源码剖析基于flutter stable 1c + 5 J O M J i.13.6
新的版本6 j K ; :或许存在代码不一致的当地,比方updateChild(…),可是逻辑都是相同的,能够定心食用

从源码看flutter(二):Element篇