Flutter框架分析(五)-Widget,Element,RenderObject树

1. 前言

在本系列文章的前面三篇文章中,已经分别介绍Flutter的三种核心元素:WidgetElement,和RenderObject。并介绍了它们之间的关系。在这篇文章中,将在前面文章的基础上,对Flutter中很是出名的概念“WidgetElementRenderObject树”进行剖析,以此加深读者对Flutter Framework的理解。markdown

2. Widget,Element,RenderObject树的概念和做用

学习Flutter的时候常常会遇到一个概念:WidgetElementRenderObject树。那什么是WidgetElementRenderObject树呢?Flutter中的WidgetElementRenderObject树指的是:Widget Tree, Element Tree, RenderObject Tree。从前面几篇文章能够知道,它们的做用分别以下:架构

  • Widget Tree

Widget Tree是整个UI界面的配置,Flutter开发者经过Widget Tree告诉Framework想要绘制的UI界面是什么样的,这棵树是咱们主要打交道的对象。app

  • Element Tree

Element Tree是经过Widget Tree生成的,其主要做用是维护UI元素的树形结构,并将WidgetRenderObject关联到树上。框架

  • RenderObject Tree

RenderObject Tree也是经过Widget Tree生成的,其主要做用是负责界面的绘制和布局,是属于底层系统,Flutter开发者通常不须要直接操做该树。 为了加深对这WidgetElementRenderObject树的理解,下面将使用一个例子进行讲解。less

3. Widget,Element,RenderObject树示例

示例代码以下:ide

class TreeTest extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Center(
        child: Text("tree test", textDirection: TextDirection.ltr)
    );
  }
}
复制代码

该示例很简单,在页面中间显示一个Text,文案为tree test。其运行结果以下:函数

image.png

其代码对应的Widget TreeElement TreeRenderObject Tree的示意图以下:oop

image.png

结合本系列文章的前面三篇:Flutter框架分析(二)--WidgetFlutter框架分析(三)--ElementFlutter框架分析(四)-RenderObject。由上图能够看出:布局

  1. Widget Tree中一个Widget,对应Element Tree中一个Element
  2. Element Tree中的Element,未必有对应RenderObject,例如StatelessElement所属的ComponentElement,就没有对应的RenderObject,其是用于组合更基础的Element
  3. Widget Tree的根节点是RenderObjectToWidgetAdapter,其对应的ElementRenderObjectToWidgetElement,这是Element Tree的根节点,对应的RenderObjectRenderView,这是RenderObject Tree的根节点。

在上图中一个值得注意的地方是Text,从图中能够看到它有个子WidgetRichText,可是咱们在示例中只定义了一个Text组件,这是怎么回事呢?咱们能够追踪下源码。post

Text的定义以下:

class Text extends StatelessWidget 复制代码

所以它只是一个用于组合其余更基础的Widget,真正控制文案属性的是在build函数中定义的RichText,其源码以下:

@override
Widget build(BuildContext context) {
  final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
  TextStyle effectiveTextStyle = style;
  if (style == null || style.inherit)
    effectiveTextStyle = defaultTextStyle.style.merge(style);
  if (MediaQuery.boldTextOverride(context))
    effectiveTextStyle = effectiveTextStyle.merge(const TextStyle(fontWeight: FontWeight.bold));
  Widget result = RichText(
    textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start,
    textDirection: textDirection, // RichText uses Directionality.of to obtain a default if this is null.
    locale: locale, // RichText uses Localizations.localeOf to obtain a default if this is null
    softWrap: softWrap ?? defaultTextStyle.softWrap,
    overflow: overflow ?? defaultTextStyle.overflow,
    textScaleFactor: textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
    maxLines: maxLines ?? defaultTextStyle.maxLines,
    strutStyle: strutStyle,
    textWidthBasis: textWidthBasis ?? defaultTextStyle.textWidthBasis,
    textHeightBehavior: textHeightBehavior ?? defaultTextStyle.textHeightBehavior ?? DefaultTextHeightBehavior.of(context),
    applyTextScaleFactorToWidgetSpan: _applyTextScaleFactorToWidgetSpan,
    text: TextSpan(
      style: effectiveTextStyle,
      text: data,
      children: textSpan != null ? <InlineSpan>[textSpan] : null,
    ),
  );
  if (semanticsLabel != null) {
    result = Semantics(
      textDirection: textDirection,
      label: semanticsLabel,
      child: ExcludeSemantics(
        child: result,
      ),
    );
  }
  return result;
}
复制代码

因此在Widget Tree中能够看到Text的child是RichText

4. Widget和Element的对应关系

上文已经说过,Widget Tree中的WidgetElement Tree中的Element关系是一一对应的,可是咱们在Flutter框架分析(二)--Widget一文中曾经说过,一个Widget对象能够对应多个Element对象。那这不是互相矛盾了吗?其实不是的,一个Widget对象并不等同于一个Widget Tree中的Widget对象,由于在Widget Tree中还有个位置属性(Slot)。一个Widget对象加上其在Widget Tree中的位置,才相对于一个Widget Tree中的Widget对象。

如下是一个Widget对象对应多个Element对象的一个示例:

class SameWidgetMultiElementWidget1 extends StatefulWidget {
  @override
  _SameWidgetMultiElementWidgetState createState() => new _SameWidgetMultiElementWidgetState();
}

class _SameWidgetMultiElementWidgetState extends State<SameWidgetMultiElementWidget1> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    Text testText = Text("multi element");
    return Column(
      children: <Widget>[
        testText,
        testText,
      ],
    );
  }
}
复制代码

其对应的Widget TreeElement Tree以下:

image.png

在上面示例中,左边是Widget Tree,右边是Element Tree。在Widget Tree中,Column有两个子节点,可是这两个子节点使用的是同一个Text对象,所以这个Text对象出如今两个不一样的位置(Slot),此时在Element Tree会生成两个不一样的StatelessElement对象,即一个Text对象对应两个StatelessElement对象。

注意该Widget TreeElement Tree未包含根节点。

5. 小结

本文主要介绍了Flutter中的常见概念:Widget Tree, Element Tree, RenderObject Tree。并经过一个例子,讲解了这三棵树之间的关系。本文的重点以下:

  1. Widget Tree中一个Widget,对应Element Tree中一个Element,可是一个Widget可能对应多个Element,由于一个Widget和它在Widget Tree中的位置一块儿才对应Widget Tree中的一个Widget
  2. Element Tree中的Element,未必有对应RenderObject,例如StatelessElement所属的ComponentElement,就没有对应的RenderObject,其是用于组合更基础的Element
  3. Widget Tree的根节点是RenderObjectToWidgetAdapter,其对应的ElementRenderObjectToWidgetElement,这是Element Tree的根节点,对应的RenderObjectRenderView,这是RenderObject Tree的根节点。

6. 参考文档

Flutter architectural overview

7. 相关文章

Flutter框架分析(一)--架构总览
Flutter框架分析(二)-- Widget
Flutter框架分析(三)-- Element
Flutter框架分析(四)-RenderObject
Flutter框架分析(六)-Constraint
Flutter框架分析(七)-relayoutBoundary
Flutter框架分析(八)-Platform Channel
Flutter框架分析- Parent Data
Flutter框架分析 -InheritedWidget

相关文章
相关标签/搜索