Flutter渲染过程中有三棵树:Widget树Render树Element树

Widget树

什么是Widget树呢,其实在我们的Android Studio中能够直观的看到某一个页面渲染之后Widget树的样子,我们以聊天界面为例看一下页面的Widget树

点击Android Studio右侧的Flutter Inspector工具,就可以呈现出当前页面的Widget树的层级关系:
image.png
需要注意的是,Flutter的渲染引擎并不是来直接渲染Widget树的,因为如果直接渲染Widget树,那么一旦build方法执行,那么整个Widget都需要重新渲染将会是非常消耗性能的(Widget是经常发生变化的);

Render树

除了Widget树,还有一个Render树其实**Flutter**引擎渲染的是**RenderObject树**,它里边是一个个的RenderObject的对象;并非所有的Widget都会生成RenderObject,比如Container;只有最终继承自RenderObjectWidget的才会生成RenderObject,比如Column

RenderObjectWidget中有几个需要我们注意的地方,我们来看一下RenderObjectWidget的源码:

  1. abstract class RenderObjectWidget extends Widget {
  2. const RenderObjectWidget({ Key? key }) : super(key: key);
  3. @override
  4. @factory
  5. RenderObjectElement createElement();
  6. @protected
  7. @factory
  8. RenderObject createRenderObject(BuildContext context);
  9. @protected
  10. void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }
  11. @protected
  12. void didUnmountRenderObject(covariant RenderObject renderObject) { }
  13. }

RenderObjectWidget中有createElement()createRenderObject()两个抽象方法;这两个方法需要RenderObjectWidget的子类来实现,我们可以通过Stack的继承关系来查看发现RenderObjectWidget的子类:

  • 子类MultiChildRenderObjectWidget中实现了createElement方法:

    1. @override
    2. MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this);

    MultiChildRenderObjectElement继承自RenderObjectElement

  • 子类Stack中实现了createRenderObject方法:

    1. @override
    2. RenderStack createRenderObject(BuildContext context) {
    3. assert(_debugCheckHasDirectionality(context));
    4. return RenderStack(
    5. alignment: alignment,
    6. textDirection: textDirection ?? Directionality.maybeOf(context),
    7. fit: fit,
    8. clipBehavior: overflow == Overflow.visible ? Clip.none : clipBehavior,
    9. );
    10. }

    在这个方法中创建了一个RenderStack,而根据RenderStack的继承链发现其继承自RenderObject
    综上,继承自RenderObjectWidget的对象,既会创建Element树,又会创建RenderObject树

    Element树

    我们根据继承链可以发现StatelessWidget直接继承自Widget,其内部没有RenderObject对象,但是有Element对象:
    image.png
    Widget的源码中存在createElement()的抽象方法,也就是所有继承自Widget的对象都有Element对象:Widget树Element树是一一对应的关系;

同样的,在StatefulWidget中实现了相同的方法:
image.png
我们在StatefulWidget中的createElement方法打上断点,然后使用Debug模式运行项目,查看一下断点的情况:
image.png
我们发现,从整个项目运行开始,第一个调用createElement的是MaterialApp这个组件;此时的this就是MaterialApp

那么如果,我们将断点打在StatelessWidget中,我们来看一下执行的情况:
image.png
此时的this将会指向MyApp;我们的main.dart文件如下:
image.png
这个时候,我们单步执行一下:
image.png
这个时候代码将会执行到:
image.png
此处接下来将会执行mount方法,我们来看一下mount方法,我们主要看一下注释:
image.png
从注释我们可以得知,当有一个新的Element被添加到Element树的时候,mount方法将会被调用;因为WidgetElement是一一对应的,所以我们也可以理解为当有一个Widget被创建的时候,mount方法就会被调用;

新添加的Element与原来的旧Element是有区别的;

我们从上文已经知道StatefulWidgetStatelessWidget是没有RenderObject对象的,那么我们继续查看一下有RenderObject对象的组件的mount方法:
image.png
单步断点执行:
image.png
接下来断点进入mount方法:
image.png
我们发现此时的mount方法调用了super.mount,我们进入super.mount方法:
image.png
我们发现在super.mount方法中调用了creatRenderObject方法,此时的mount方法是由RenderObjectElement实现的;

那么我们可以得出一下结论:

Flutter渲染流程中,最终是针对Render树中的对象进行渲染;当一个Widget被创建时,都会通过createElement方法创建一个Element加入到Element树中,然后会执行mount方法,此时如果含有RenderObject(Element是否继承自RenderObjectElement),则会在mount方法中通过createRenderObject方法创建RenderObject树,反之则不创建;