[关闭]
@TryLoveCatch 2019-10-28T15:51:49.000000Z 字数 14122 阅读 2365

Flutter之三棵树

flutter


深入了解Flutter界面开发
帝国的纷争-Flutter-UI绘制解析
Flutter Dart Framework原理简解
Flutter 视图树
Flutter中的层级蛋糕

很好:Joey的Flutter之旅 - (3) 布局构建、渲染、绘制机制浅析

Flutter 的渲染逻辑及和 Native 通信

BuildContext

Flutter BuildContext 探究
Flutter | 深入理解BuildContext

BuildContext objects are actually Element objects. The BuildContext interface is used to discourage direct manipulation of Element objects.

BuildContext对象实际上就是Element对象,BuildContext 接口用于阻止对 Element 对象的直接操作。

每个Widget的build(BuildContext context)方法中传递的context就是实现了BuildContext接口的Element

Key

理解 Flutter 中的 Key
Flutter | 深入浅出Key

层级

Flutter之Widget层级介绍

Widget

  1. abstract class Widget extends DiagnosticableTree {
  2. const Widget({ this.key });
  3. final Key key;
  4. @protected
  5. Element createElement();
  6. static bool canUpdate(Widget oldWidget, Widget newWidget) {
  7. return oldWidget.runtimeType == newWidget.runtimeType
  8. && oldWidget.key == newWidget.key;
  9. }
  10. }

Widget是一个抽象类,它只有两个方法:

我们发现Widget并没有和RenderObject扯上关系,难道RenderObject是在Element里面创建的吗?
并不是,这就涉及到了Widget的一个子类RenderObjectWidget了

RenderObjectWidget

RenderObjectWidget是一个非常重要的类,如果不直接或间接继承该类,Widget就无法显示在界面上。

  1. abstract class RenderObjectWidget extends Widget {
  2. ...
  3. const RenderObjectWidget({ Key key }) : super(key: key);
  4. /// RenderObjectWidget对应着RenderObjectElement及其子类
  5. @override
  6. RenderObjectElement createElement();
  7. /// 创建一个RenderObject对象
  8. @protected
  9. RenderObject createRenderObject(BuildContext context);
  10. /// 更新renderObject
  11. @protected
  12. void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }
  13. /// 将renderObject从render树中移除
  14. @protected
  15. void didUnmountRenderObject(covariant RenderObject renderObject) {}
  16. }

RenderObjectWidget是一个继承自Widget的子类,但它比Widget多几个方法。

RenderObject主要是将视图绘制成不同的图层,然后再显示在屏幕上,所以:

RenderObjectWidget有三个比较重要的子类:

Widget 对应的Element 说明 举例
LeafRenderObjectWidget LeafRenderObjectElement Widget树的叶子节点,用于没有子节点的widget,通常基础组件都属于这一类 Image,_AndroidPlatformView
SingleChildRenderObjectWidget SingleChildRenderObjectElement 包含一个子Widget Center,Padding,Container
MultiChildRenderObjectWidget MultiChildRenderObjectElement 包含多个子Widget,一般都有一个children参数,接受一个Widget数组 Row,Column,Stack

StatelessWidget

  1. abstract class StatelessWidget extends Widget {
  2. /// Initializes [key] for subclasses.
  3. const StatelessWidget({ Key key }) : super(key: key);
  4. @override
  5. StatelessElement createElement() => StatelessElement(this);
  6. @protected
  7. Widget build(BuildContext context);
  8. }

StatelessWidget,我们应该无比熟悉了,

StatefulWidget

  1. abstract class StatefulWidget extends Widget {
  2. @override
  3. StatefulElement createElement() => StatefulElement(this);
  4. @protected
  5. State createState();
  6. }

State

  1. abstract class State<T extends StatefulWidget> extends Diagnosticable {
  2. T get widget => _widget;
  3. T _widget;
  4. BuildContext get context => _element;
  5. StatefulElement _element;
  6. bool get mounted => _element != null;
  7. void initState() { }
  8. void didUpdateWidget(covariant T oldWidget) { }
  9. void setState(VoidCallback fn) {
  10. final dynamic result = fn() as dynamic;
  11. _element.markNeedsBuild();
  12. }
  13. void deactivate() { }
  14. void dispose() { }
  15. Widget build(BuildContext context);
  16. void didChangeDependencies() { }
  17. }

从源码可见,State持有对应的Widget和Element.

InheritedWidget

Element

  1. abstract class Element extends DiagnosticableTree implements BuildContext {
  2. Element _parent;
  3. Widget _widget;
  4. BuildOwner _owner;
  5. dynamic _slot;
  6. void visitChildren(ElementVisitor visitor) { }
  7. Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
  8. }
  9. void mount(Element parent, dynamic newSlot) {
  10. }
  11. void unmount() {
  12. }
  13. void update(covariant Widget newWidget) {
  14. }
  15. @protected
  16. Element inflateWidget(Widget newWidget, dynamic newSlot) {
  17. ...
  18. final Element newChild = newWidget.createElement();
  19. newChild.mount(this, newSlot);
  20. return newChild;
  21. }
  22. void markNeedsBuild() {
  23. if (dirty)
  24. return;
  25. _dirty = true;
  26. owner.scheduleBuildFor(this);
  27. }
  28. void rebuild() {
  29. if (!_active || !_dirty)
  30. return;
  31. performRebuild();
  32. }
  33. @protected
  34. void performRebuild();
  35. }

从上面代码中,我们可以发现Element在每一帧内都会在以下几种状态之间转换。

ComponentElement

  1. abstract class ComponentElement extends Element {
  2. ComponentElement(Widget widget) : super(widget);
  3. Element _child;
  4. @override
  5. void performRebuild() {
  6. Widget built;
  7. built = build();
  8. _child = updateChild(_child, built, slot);
  9. }
  10. Widget build();
  11. }

ComponentElement表示当前这个Element是用来组合其他Element的,它也许你不熟悉,但是他的两个子类,你肯定知道

StatelessElement

  1. class StatelessElement extends ComponentElement {
  2. @override
  3. Widget build() => widget.build(this);
  4. @override
  5. void update(StatelessWidget newWidget) {
  6. super.update(newWidget);
  7. _dirty = true;
  8. rebuild();
  9. }
  10. }

其build()函数直接调用的就是StatelessWidget.build()。现在你知道你写在StatelessWidget里的build()是在哪里被调用的了吧。而且你看,build()函数的入参是this。我们都知道这个函数的入参应该是BuildContext类型的。这个入参其实就是这个StatelessElement。

StatefulElement

  1. class StatefulElement extends ComponentElement {
  2. /// Creates an element that uses the given widget as its configuration.
  3. StatefulElement(StatefulWidget widget)
  4. : _state = widget.createState(),
  5. super(widget) {
  6. _state._element = this;
  7. _state._widget = widget;
  8. }
  9. @override
  10. Widget build() => state.build(this);
  11. @override
  12. void _firstBuild() {
  13. final dynamic debugCheckForReturnedFuture = _state.initState()
  14. _state.didChangeDependencies();
  15. super._firstBuild();
  16. }
  17. @override
  18. void deactivate() {
  19. _state.deactivate();
  20. super.deactivate();
  21. }
  22. @override
  23. void unmount() {
  24. super.unmount();
  25. _state.dispose();
  26. _state._element = null;
  27. _state = null;
  28. }
  29. @override
  30. void didChangeDependencies() {
  31. super.didChangeDependencies();
  32. _state.didChangeDependencies();
  33. }
  34. }

在StatefulElement构造的时候会调用对应StatefulWidget的createState()函数。也就是说State是在实例化StatefulElement的时候被实例化的。并且State实例会被这个StatefulElement实例持有。
从这里也可以看出为什么StatefulWidget的状态要由单独的State管理,每次刷新的时候可能会有一个新的StatefulWidget被创建,但是State实例是不变的,因为Element没有改变。

InheritedElement

RenderObjectElement

  1. abstract class RenderObjectElement extends Element {
  2. RenderObject _renderObject;
  3. @override
  4. void mount(Element parent, dynamic newSlot) {
  5. super.mount(parent, newSlot);
  6. _renderObject = widget.createRenderObject(this);
  7. attachRenderObject(newSlot);
  8. _dirty = false;
  9. }
  10. @override
  11. void unmount() {
  12. super.unmount();
  13. widget.didUnmountRenderObject(renderObject);
  14. }
  15. @override
  16. void update(covariant RenderObjectWidget newWidget) {
  17. super.update(newWidget);
  18. widget.updateRenderObject(this, renderObject);
  19. _dirty = false;
  20. }
  21. @override
  22. void performRebuild() {
  23. widget.updateRenderObject(this, renderObject);
  24. _dirty = false;
  25. }
  26. @protected
  27. void insertChildRenderObject(covariant RenderObject child, covariant dynamic slot);
  28. @protected
  29. void moveChildRenderObject(covariant RenderObject child, covariant dynamic slot);
  30. @protected
  31. void removeChildRenderObject(covariant RenderObject child);
  32. }

RenderObject

  1. abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
  2. void markNeedsLayout() {
  3. ...
  4. }
  5. void markNeedsPaint() {
  6. ...
  7. }
  8. void layout(Constraints constraints, { bool parentUsesSize = false }) {
  9. ...
  10. if (sizedByParent) {
  11. performResize();
  12. }
  13. ...
  14. performLayout();
  15. ...
  16. }
  17. void performResize();
  18. void performLayout();
  19. void paint(PaintingContext context, Offset offset) { }
  20. }

别看RenderObject源码那么多。但核心方法其实只有两个。

RenderBox

RenderSliver

小结

Flutter之Element更新机制简单分析
Flutter之Widget层级介绍
Flutter框架分析(三)-- Widget,Element和RenderObject

注意:

布局构建的基本原理

Joey的Flutter之旅 - (3) 布局构建、渲染、绘制机制浅析

建立

1、创建两个 Widget,一个绿色的 Rectangle Widget,他携带一个 Child:一个蓝色的 Circle Widget。

2、Rectangle Widget 调用 createElement() 方法创建了自己对应的 Element。

3、Rectangle Widget 通过 createRenderObject() 创建了相应的 Render Object —— “RenderRectangle”,同时,Element 持有了两者的引用,将三者联系起来,这很重要。

4、子 Widget 即蓝色的 Circle Widget 调用 createElement() 方法创建了自己对应的 Element,通过 mount() 方法将 Element 挂载到了父 Element 上。

5、蓝色 Circle Widget 的 Render Object 也创建完成后,Element 通过自己的 attachRenderObject() 方法将 RenderCircle attach 到了 RenderRectangle 的一个 slot (插槽) 上。

更新

Widget 类型不变

这一种情形中,新的 Rectangle 和 Circle 仅仅是颜色这一属性发生了变化,Widget 的类型并没有变化,那么 Flutter 的布局会如何响应?

1、黄色 Rectangle Widget 并没有新建 Element,而是复用了之前 Rectangle 的 Element,由于 Rectangle 并没有改变 Widget 类型,所以 Element 只需要根据新的 Widget 修改自身的颜色配置参数作为新的 Widget 即可。

2、相应的,RenderRectangle 也只是做了颜色数据上的改变,并没有重新建立 Render Object。

3、最终,通过 Widget 的重建以及对应 Element 以及 Render Object 的修改完成了渲染树的重建,之后会进行具体的重绘过程。

Widget 类型改变


在这一种情形中,子 Widget 类型由 Circle 变为了 Triangle,这种情况下,Flutter 的布局重建会有怎样的变化呢?

1、由于 Rectangle Widget 类型并没有变化,所以并没有引起 Element 与 Render Object 的实际变化。Element 照常持有了新 Rectangle Widget 与 RenderRectangle 的引用。

2、此时的 Triangle Widget 由于类型发生了变化,便不能和之前一样复用 由 Rectangle 类型创建的 Element 与 Render Object 了。Element 与 Render Object 被“摘”下来,值得一提的是,Flutter 仍可以复用他们,只不过在这一流程中,他们被抛弃了。

3、Triangle Widget 重新走了一遍 createElement() 与 createRenderObject() 的流程,并且分别挂载到了父节点的 slot 上面。新的 Element 再次持有了 Triangle Widget 与 RenderTriangle 的引用。

局部更新

上面几种布局重建,需要调用 runApp() 方法进行,而且这种方式会从根节点开始进行重建,上面的例子 Widget 数量很少,所以影响不大,但是如果成百上千个 Widget,那么每次将整个 tree 重建就相当不划算了,我们希望 Flutter 能够实现局部的布局重建。

于是,熟悉的 StatefulWidget & State 的形式被设计出来了,StatefulWidget 并不是 RenderObjectWidget,也就是说,他不是用来形成具体的渲染对象的,他只是用来维持特定的 state,所以右边的渲染树中你找不到 StatefulWidget 的影子。state 是由 StatefulWidget 创建的,而其对应的 StatefulElement 会持有这个 state,所以无论StatefulWidget重建多少次,只要StatefulElement没有重建,那么state就不会改变。
调用setState(),重建开始的位置是从 StatefulWidget 往下的位置,上面的 Rectangle 并没有受影响,所以这种方式实现了布局的局部重建。

Element更新原则

我们以SingleChildRenderObjectElement为例:

  1. class SingleChildRenderObjectElement extends RenderObjectElement {
  2. SingleChildRenderObjectElement(SingleChildRenderObjectWidget widget) : super(widget);
  3. //对应子Widget的Element对象。
  4. Element _child;
  5. @override
  6. void mount(Element parent, dynamic newSlot) {
  7. super.mount(parent, newSlot);
  8. //创建子child Widget对应的Element对象,第三个参数传null
  9. _child = updateChild(_child, widget.child, null);
  10. }
  11. }

我们来看Element#updateChild()

  1. Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
  2. assert(() {
  3. if (newWidget != null && newWidget.key is GlobalKey) {
  4. final GlobalKey key = newWidget.key;
  5. key._debugReserveFor(this);
  6. }
  7. return true;
  8. }());
  9. if (newWidget == null) {
  10. if (child != null)
  11. deactivateChild(child);
  12. return null;
  13. }
  14. if (child != null) {
  15. if (child.widget == newWidget) {
  16. if (child.slot != newSlot)
  17. updateSlotForChild(child, newSlot);
  18. return child;
  19. }
  20. if (Widget.canUpdate(child.widget, newWidget)) {
  21. if (child.slot != newSlot)
  22. updateSlotForChild(child, newSlot);
  23. child.update(newWidget);
  24. assert(child.widget == newWidget);
  25. assert(() {
  26. child.owner._debugElementWasRebuilt(child);
  27. return true;
  28. }());
  29. return child;
  30. }
  31. deactivateChild(child);
  32. assert(child._parent == null);
  33. }
  34. return inflateWidget(newWidget, newSlot);
  35. }

注意:child是Element对象,newWidget是Widget对象

- newWidget为空 newWidget不为空
child为null 返回null 创建new Element
child不为null 移除旧的widget,返回null. 若旧的child Element 可以更新(canUpdate)则更新并将其返回,否则返回一个新的 Element.

update(newWidget)

  1. void update(covariant Widget newWidget) {
  2. assert(_debugLifecycleState == _ElementLifecycle.active
  3. && widget != null
  4. && newWidget != null
  5. && newWidget != widget
  6. && depth != null
  7. && _active
  8. && Widget.canUpdate(widget, newWidget));
  9. _widget = newWidget;
  10. }

仅仅是更新了Element里面的_widget

canUpdate

该方法主要是判断Widget是否可更新,从而判断出来Element是否可以复用,如果可以复用,就调用Element#update(newWidget)来更新Element持有的Widget

  1. abstract class Widget extends DiagnosticableTree {
  2. const Widget({ this.key });
  3. final Key key;
  4. ···
  5. static bool canUpdate(Widget oldWidget, Widget newWidget) {
  6. return oldWidget.runtimeType == newWidget.runtimeType
  7. && oldWidget.key == newWidget.key;
  8. }
  9. }

我们来看一个例子:

我们在界面是显示两个色块,然后点击fab可以切换两个色值,我们分别用StatelessWidget和StatefulWidget来实现:

StatelessWidget实现

StatelessContainer

  1. class StatelessContainer extends StatelessWidget {
  2. final Color color = RandomColor().randomColor();
  3. @override
  4. Widget build(BuildContext context) {
  5. return Container(
  6. width: 100,
  7. height: 100,
  8. color: color,
  9. );
  10. }
  11. }

我们现在将这个Widget展示到界面上:

  1. class Screen extends StatefulWidget {
  2. @override
  3. _ScreenState createState() => _ScreenState();
  4. }
  5. class _ScreenState extends State<Screen> {
  6. List<Widget> widgets = [
  7. StatelessContainer(),
  8. StatelessContainer(),
  9. ];
  10. @override
  11. Widget build(BuildContext context) {
  12. return Scaffold(
  13. body: Center(
  14. child: Row(
  15. mainAxisAlignment: MainAxisAlignment.center,
  16. children: widgets,
  17. ),
  18. ),
  19. floatingActionButton: FloatingActionButton(
  20. onPressed: switchWidget,
  21. child: Icon(Icons.undo),
  22. ),
  23. );
  24. }
  25. switchWidget(){
  26. widgets.insert(0, widgets.removeAt(1));
  27. setState(() {});
  28. }
  29. }
StatefulWidget实现

渲染

Flutter 的渲染逻辑及和 Native 通信

拾遗

壹 Widget是immutable的,怎么更新呢?

Widget是轻量级的,就是一个简单的数据容器,每次更新的时候,Widget都会被重建,也就是重新生成一个对象实例。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注