image.png

1 Widget

  • Widget就是一个个描述文件,这些描述文件在我们进行状态改变时会不断的build。
  • 但是对于渲染对象来说,只会使用最小的开销来更新渲染界面。

Padding是一个Widget,并且继承自SingleChildRenderObjectWidget
继承关系如下:

Padding -> SingleChildRenderObjectWidget -> RenderObjectWidget -> Widget

我们之前在创建Widget时,经常使用StatelessWidget和StatefulWidget,这种Widget只是将其他的Widget在build方法中组装起来,并不是一个真正可以渲染的Widget。
在Padding的类中,我们找不到任何和渲染相关的代码,这是因为Padding仅仅作为一个配置信息,这个配置信息会随着我们设置的属性不同,频繁的销毁和创建。

2 RenderObject

  • 渲染树上的一个对象
  • RenderObject层是渲染库的核心, 真正负责渲染widget。
  • 其中有markNeedsLayout, performLayout, markNeedsPaint paint等方法

Padding里面的代码,有一个非常重要的方法createRenderObject:

  • 这个方法其实是来自RenderObjectWidget的类,在这个类中它是一个抽象方法;
  • 抽象方法是必须被子类实现的,但是它的子类SingleChildRenderObjectWidget也是一个抽象类,所以可以不实现父类的抽象方法
  • 但是Padding不是一个抽象类,必须在这里实现对应的抽象方法,而它的实现就是下面的实现
    1. @override
    2. RenderPadding createRenderObject(BuildContext context) {
    3. return RenderPadding(
    4. padding: padding,
    5. textDirection: Directionality.of(context),
    6. );
    7. }
    RenderPadding的继承关系是什么呢?

    RenderPadding -> RenderShiftedBox -> RenderBox -> RenderObject

我们来具体查看一下RenderPadding的源代码:

  • 如果传入的_padding和原来保存的value一样,那么直接return;
  • 如果不一致,调用_markNeedResolution,而_markNeedResolution内部调用了markNeedsLayout;
  • 而markNeedsLayout的目的就是标记在下一帧绘制时,需要重新布局performLayout;
  • 如果我们找的是Opacity,那么RenderOpacity是调用markNeedsPaint,RenderOpacity中是有一个paint方法的;

    1. set padding(EdgeInsetsGeometry value) {
    2. assert(value != null);
    3. assert(value.isNonNegative);
    4. if (_padding == value)
    5. return;
    6. _padding = value;
    7. _markNeedResolution();
    8. }

    3 Element

  • Element是一个Widget的实例,在树中详细的位置。

  • Widget描述和配置子树的样子,而Element实际去配置在Element树中特定的位置。

大量的Widget在树结构中存在引用关系,但是Widget会被不断的销毁和重建,那么意味着这棵树非常不稳定; 由Element来维系整个Flutter应用程序的树形结构的稳定

Element什么时候创建?
在每一次创建Widget的时候,会创建一个对应的Element,然后将该元素插入树中。

  • Element保存着对Widget的引用;

在SingleChildRenderObjectWidget中,我们可以找到如下代码:

  • 在Widget中,Element被创建,并且在创建时,将this(Widget)传入了;
  • Element就保存了对Widget的引用;

    1. @override
    2. SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
  • 在创建完一个Element之后,Framework会调用mount方法来将Element插入到树中具体的位置

  • 在调用mount方法时,会同时使用Widget来创建RenderObject,并且保持对RenderObject的引用