1. Introduction

In the previous three articles of this series, three core elements of Flutter were introduced: Widget, Element, and RenderObject. The relationship between them is also introduced. In this article, we will analyze the well-known concepts of Flutter, including “Widget, Element and RenderObject tree”, based on the previous article, in order to deepen readers’ understanding of the Flutter Framework.

2. Concepts and functions of Widget, Element and RenderObject trees

Learning about Flutter often comes across one concept: Widget, Element, RenderObject tree. So what are Widget, Element, RenderObject trees? The Widget, Element, and RenderObject trees in Flutter refer to the Widget Tree, Element Tree, and RenderObject Tree. As you can see from the previous articles, their functions are as follows:

  • Widget Tree

The Widget Tree is the configuration of the entire UI that the Flutter developer uses to tell the Framework what the UI should look like. The Widget Tree is the main object that we deal with.

  • Element Tree

The Element Tree is generated by Widget Tree. Its main function is to maintain the Tree structure of UI elements and associate widgets and RenderObjects to the Tree.

  • RenderObject Tree

The RenderObject Tree is also generated by the Widget Tree. It is responsible for the rendering and layout of the interface and is the underlying system. Flutter developers generally do not need to operate the Tree directly. To further understand the Widget, Element, and RenderObject trees, we will use an example below.

3. Widget, Element, RenderObject tree examples

Example code is as follows:

class TreeTest extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Center(
        child: Text("tree test", textDirection: TextDirection.ltr) ); }}Copy the code

The example is simple, displaying a Text in the middle of the page with the Text as Tree Test. The running results are as follows:

The corresponding Widget Tree, Element Tree and RenderObject Tree are shown as follows:

Combine the previous three articles in this series: Flutter framework analysis (ii) –Widget, Flutter framework analysis (III) –Element, Flutter framework analysis (IV) — RenderObject. As can be seen from the figure above:

  1. Widget TreeIn aWidget, corresponding toElement TreeIn aElement.
  2. Element TreeIn theElementThere may not be a correspondingRenderObject, e.g.StatelessElementSubordinate to theComponentElementThere is no correspondingRenderObjectIt is more basic for compositionElement.
  3. Widget TreeIs the root node ofRenderObjectToWidgetAdapterAnd its correspondingElementisRenderObjectToWidgetElement, this isElement TreeThe root node ofRenderObjectisRenderView, this isRenderObject TreeThe root node of.

One notable feature in the figure above is Text, which you can see has a sub-widget: RichText, but we only defined a Text component in the example. What’s going on here? We can trace the source code.

Text is defined as follows:

class Text extends StatelessWidget
Copy the code

So it’s just a Widget for composing other, more basic widgets. What really controls the text attributes is the RichText defined in the build function, which has the following source code:

@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 nullsoftWrap: 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;
}
Copy the code

So you can see in Widget Tree that the child of Text is RichText.

4. Mapping between Widgets and Elements

As mentioned above, the Widget in the Widget Tree corresponds to the Element in the Element Tree one-to-one. However, we mentioned that one Widget object can correspond to multiple Element objects in the Flutter framework. Doesn’t that contradict each other? No, a Widget object is not the same as a Widget object in the Widget Tree, because there are also Slot properties in the Widget Tree. A Widget object plus its position in the Widget Tree is relative to a Widget object in a Widget Tree.

Here is an example of a Widget object that corresponds to multiple Element objects:

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");
    returnColumn( children: <Widget>[ testText, testText, ], ); }}Copy the code

The corresponding Widget Tree and Element Tree are as follows:

In the example above, the Widget Tree is on the left and the Element Tree is on the right. In Widget Tree, Column has two child nodes, but the two children use the same Text object, so the Text object appears in two different slots. In this case, two different StatelessElement objects are generated in the Element Tree. That is, a Text object corresponds to two StatelessElement objects.

Notice that the Widget Tree and Element Tree do not contain the root node.

5. Summary

This article introduces the common concepts of Flutter: Widget Tree, Element Tree, RenderObject Tree. And through an example, explained the relationship between the three trees. The main points of this paper are as follows:

  1. Widget TreeIn aWidget, corresponding toElement TreeIn aElementBut aWidgetThere may be more than oneElementBecause of oneWidgetAnd it’s on theWidget TreeThe positions in theWidget TreeOne of theWidget.
  2. Element TreeIn theElementThere may not be a correspondingRenderObject, e.g.StatelessElementSubordinate to theComponentElementThere is no correspondingRenderObjectIt is more basic for compositionElement.
  3. Widget TreeIs the root node ofRenderObjectToWidgetAdapterAnd its correspondingElementisRenderObjectToWidgetElement, this isElement TreeThe root node ofRenderObjectisRenderView, this isRenderObject TreeThe root node of.

6. Reference documents

Flutter architectural overview

7. Related articles

Flutter framework Analysis (1) — Overview of the Flutter framework Analysis (2) — Widget Flutter framework analysis (3) — Element Flutter framework Analysis (4) — RenderObject -Constraint Flutter framework Analysis (7) -relayoutBoundary Flutter framework analysis (8) -Platform Channel Flutter framework analysis – Parent Data Flutter framework Analysis -InheritedWidget