注:本文代码基于Flutter SDK 3.13.5

一、Widget的分类

作为一个初学者,我们知道在Flutter中万物皆Widget,这恰恰说明了WidgetFlutter中的一个重要性,那么以Android开发者的角度来看,Flutter中的Widget是否等同于Android中的View呢?或者说Widget能否像Android中的UI控件一样分为ViewGroupView两种形式呢?比如说,将需要传入child参数的Widget可认为ViewGroup,如Container、Center等,将不需要传入child参数的Widget可认为View,如TextIcon等等,如此分类是否合理呢?

OK,带着疑问,我们先来看下Widget的部分注释。

1
2
3
4
5
/// Describes the configuration for an [Element].
///
/// Widgets are the central class hierarchy in the Flutter framework. A widget
/// is an immutable description of part of a user interface. Widgets can be
/// inflated into elements, which manage the underlying render tree.

翻译:Widget描述一个Element的配置。Widget是Flutter框架中的中心类层次结构。一个widget是用户界面一部分的不可变描述。Widget可以创建出Element,这些Element管理底层渲染树。

其中“be inflated into”在谷歌翻译中会被翻译为“膨胀为”,但是我认为应该翻译为“创建出”比较合理,因为在Widget源码中有一个抽象的createElement方法,就是用来创建并返回Element对象的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/// Widget的部分源码
abstract class Widget extends DiagnosticableTree {

  const Widget({ this.key });


  final Key? key;

  @protected
  @factory
  Element createElement();
}

现在,我们知道了WidgetFlutter中扮演着一个UI配置的角色,它是不可变的,Widget本身既非视图,也不会直接绘制任何内容。从这一维度上讲,Widget不完全等同于Android中的View,所以将WidgetAndroid中的UI控件一样分为ViewGroupView两种形式我认为并不合理。

那么Widget该如何分类呢?我们可以先查看Widget的所有直接子类,比对直接子类之间的区别来进行分类。

选中Widget-》点击AS菜单栏Navigate中的Type Hierarchy选项,就可以输出Widget的继承关系层次结构(使用快捷键呼出也可以,我这里的默认快捷键是F4)。

经过仔细比对Widget直接子类之间的区别,将Widget分类后如下图所示。

如上图所示,根据有无RenderObject大致可以将Widget分为RenderObjectWidget一类和非RenderObjectWidget另一类。

  • RenderObjectWidget一类

那么RenderObjectWidget是什么?看下它的部分注释说明。

1
2
3
/// RenderObjectWidgets provide the configuration for [RenderObjectElement]s,
/// which wrap [RenderObject]s, which provide the actual rendering of the
/// application.

翻译:RenderObjectWidget为RenderObjectElement提供配置,RenderObjectElement包装了RenderObject,RenderObject提供应用程序的实际渲染。

到这里已经初步知道这三颗树在Flutter中的作用了,WidgetElement提供配置,Element管理着RenderObject,实际渲染的是RenderObject

再来看下RenderObjectWidget源码。

由上图可知,RenderObjectWidget抽象类主要包含4个方法:createElementcreateRenderObjectupdateRenderObjectdidUnmountRenderObject,其中createElementcreateRenderObject为抽象方法,需要子类去实现,也就是说, RenderObjectWidget不仅要创建Element,也要创建RenderObject

下面以同样的方式查看RenderObjectWidget继承关系层次结构,又可以将RenderObjectWidget一类分为以下几类。

Widget 说明
SingleChildRenderObjectWidget SingleChildRenderObjectElement提供配置,只能传入单个ChildWidget
MultiChildRenderObjectWidget MultiChildRenderObjectElement提供配置,可以传入多个ChildWidget
LeafRenderObjectWidget LeafRenderObjectElement提供配置,没有ChildWidget
RenderObjectToWidgetAdapter RenderObjectElement树的桥梁
  • RenderObjectWidget另一类

RenderObjectWidget又可以按有无State分为StatefulWidgetStatelessWidgetProxyWidget等等,我们只重点看下StatefulWidgetStatelessWidget

首先看下StatefulWidget的部分注释。

1
/// A widget that has mutable state.

翻译:Widget具有可变状态。

StatefulWidget中主要有createElementcreateState方法,其中createElement方法返回了一个StatefulElement,而createState方法则为一个抽象方法,就是我们经常实现的一个方法。

为什么说StatefulWidget是可变的?这是因为State信息在Widget的生命周期中可能会发生变化。Widget实现者可以确保在状态更改时使用State.setState及时通知State

至于StatelessWidget,看下它的部分注释。

1
/// A widget that does not require mutable state.

翻译:不需要可变状态的Widget。

StatelessWidget主要有createElementbuild方法,其中createElement方法返回了一个StatelessElement,而build方法则为一个抽象方法,就是我们经常实现的一个方法。

二、 Element的分类

通过对上面Widget分类的了解,我们可以看出它主要是为了给Element提供配置和创建Element而生的。并且细心点就可以发现,对于上述提到的XXXWidget基本上都有对应的XXXElement,如下:

1
2
3
4
5
6
7
StatelessWidget -> StatelessElement
StatefulWidget -> StatefulElement
InheritedWidget -> InheritedElement
LeafRenderObjectWidget -> LeafRenderObjectElement
SingleChildRenderObjectWidget -> SingleChildRenderObjectElement
MultiChildRenderObjectWidget -> MultiChildRenderObjectElement
...

那么,Element到底是什么?首先看下Element的部分注释。

1
/// An instantiation of a [Widget] at a particular location in the tree.

翻译:树中特定位置的Widget实例。

通过查看Element的部分源码我们可以发现,Element中不仅持有了Widget、还持有了RenderObject,从这一角度来讲,ElementWidgetRenderObject之间沟通的桥梁。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Element的部分源码
abstract class Element extends DiagnosticableTree implements BuildContext {
  Element(Widget widget)
    : assert(widget != null),
      widget = widget;

    @override
    Widget get widget => _widget!;
    Widget? _widget;

    RenderObject? get renderObject {
      RenderObject? result;
      void visit(Element element) {
        assert(result == null);
        if (element._lifecycleState == _ElementLifecycle.defunct) {
          return;
        } else if (element is RenderObjectElement) {
          result = element.renderObject;
        } else {
          element.visitChildren(visit);
        }
      }
      visit(this);
      return result;
    }
}

下面以同样的方式查看Element继承关系层次结构,可以发现每个XXXElement基本上是通过继承ComponentElementRenderObjectElement来间接继承ElementElement分类如下图所示。

三、RenderObject的分类

通过前面的了解,我们知道RenderObject用来渲染的,那么RenderObject是什么呢?看下它的部分注释。

1
2
3
4
/// An object in the render tree.
///
/// The [RenderObject] class hierarchy is the core of the rendering
/// library's reason for being.

翻译:渲染树中的对象。RenderObject类层次结构是渲染库存在的核心。

下面以同样的方式查看RenderObject继承关系层次结构,又可以将RenderObject分为以下几类。

RenderObject 说明
RenderObjectWithChildMixin 用于渲染只有一个子对象的泛型mixin
ContainerRenderObjectMixin 用于渲染具有多个子对象的泛型mixin
RenderView 渲染树的根节点
RenderBox 二维笛卡尔坐标系中的渲染对象
RenderSliver 在视口中实现滚动效果的渲染对象的基类
RelayoutWhenSystemFontsChangeMixin RenderObjectMixin每当系统字体更改时都会调用systemFontsDidChange