“This is the second day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

preface

I have read an article about the rendering principle of Flutter, which describes the three trees of Flutter. What are the three trees of Flutter? What is the relationship between these three trees? Do frequent Widget creation and changes affect performance? And why did you create three trees?

The three trees of Flutter

The three trees are Widget, Element, and RenderObject

  • Widget tree: Holds render content, view layout information;

  • Element tree: Layout and drawing based on the layout properties of the Widget;

  • RenderObject tree: Holds the context, traversing the view tree with an Element that holds both widgets and renderObjects;

The Widget tree

Widgets are descriptions of user pages that represent Element configuration information. Flutter pages are declared by various combinations of widgets. Widgets themselves are immutable,

That is, all variables that it directly declares or inherits must be of final type. If you want to associate a variable State with a widget, consider using the StatefulWidget, which creates a State object through createState and then merges into the tree whenever it is converted to an Element.

Widgets are divided into stateless statelessWidgets and stateful statefull widgets

  • StatelessWidget: a widget that has no intermediate state changes. The StatelessWidget should be re-created to update the display content.
  • StatefullWidgetNot all widgets are immutable. Where are state changes stored? The class that flutter introduces state is used to store intermediate states by callingstate.setState()Update the entire subtree of this node and below;

State

A StatefulWidget class corresponds to a State class. State represents the State to be maintained by its corresponding StatefulWidget. State information stored in State can be:

It can be read synchronously when a Widget is built. The State can be changed during the Widget lifecycle. When the State is changed, you can manually call its setState() method to notify the Flutter framework of the State change. The Flutter framework receives the message, Its build method is re-called to rebuild the Widget tree to update the UI.

The Widget lifecycle is described in this article: juejin.cn/post/703469…

The Element tree

The Widget tree is very unstable, and often the build method is executed. Once the build method is called, it means that all other widgets that the Widget depends on are recreated. If Flutter resolves the Widget tree directly, Converting it to a RenderObject tree to render directly can be a very performance consuming process, so there must be something to absorb the inconvenience of these changes and cache them.

So there’s another Element tree. The Element tree layer abstracts Widget tree changes so that only the parts that really need to be modified can be synchronized to the real RenderObject tree, minimizing changes to the real render view and improving rendering efficiency, rather than destroying the entire render view tree rebuild.

RenderObject tree

Each node in the rendering tree is an object inherited from the RenderObject class. In Element, createRenderObject generates RenderObject. Internally, the object provides several properties and methods to help components in the framework layer how to lay out renderings. RenderObject is used to lay out and draw application interfaces, storing information such as the size and layout of elements. Instantiating a RenderObject can be very performance expensive.

Flutter has three tree relationships

First of all, we need to know what is the process of starting the App to create a tree:

  • Create the widget tree
  • Call runApp(rootWidget) and pass rootWidget to rootElement as a child of rootElement to generate the Element tree, which in turn generates the Render tree

  • Widgets: Stores render content, view layout information, and preferably immutable properties of widgets

  • Element: Holds the context, traversing the view tree through Element, which holds both widgets and renderObjects

  • RenderObject: Creates a layout based on the Widget’s layout properties, and paints the content passed to the Widget

The general flow from creation to rendering is as follows:

  • Generate from widgetsElementAnd then create the correspondingRenderObjectTo the Element.renderObject property, and finally passRenderObjectTo complete theLayout arrangement and drawing.
  • An Element is a Widget located in the UI treeInstantiate an object, most elements onlyThe onlyRenderObject, but some elements have multiple child nodes, such as classes that inherit from RenderObjectElement, for exampleMultiChildRenderObjectElement.
  • Finally, all elements’ renderObjects form a Tree called the Render TreeRender tree.

To summarize, we can think of Flutter’s UI system as consisting of three trees: Widget tree, Element tree, and RenderObject tree. Their dependencies are that the Element tree is generated from the Widget tree, the render tree is generated from the Element tree, and the final UI tree is actually made up of individual Element nodes.

Do frequent Widget creation and changes affect performance?

Frequent creation and change of widgets does not affect performance. The main performance impact is to reuse elements to reduce frequent creation and destruction of RenderObject objects, since frequent instantiation and destruction of RenderObjects can have a significant impact on performance.

static bool canUpdate(Widget oldWidget, Widget newWidget) {
  return oldWidget.runtimeType == newWidget.runtimeType
      && oldWidget.key == newWidget.key;
}
Copy the code

Flutter follows a basic principle: determine whether the new Widget is of the same type and key as the old one by looking at the value returned by the canUpdate function:

  • If false is returned, remove the Widget, Element, and RenderObject from their trees (including their subtrees) and create new objects.
  • If true, just modify the configuration in the RenderObject and continue traversing;

Write a key for Flutter to illustrate that there will be reuse: juejin.cn/post/705000…

Why three trees?

Reusing an Element is important for performance because it has two critical pieces of data: a Stateful widget state object and the underlying RenderObject. This advantage may not be realized when the structure of the application is simple, but once the application becomes complex and the number of elements that make up the page increases, the cost of recreating three trees is high, so updates need to be minimized. When Flutter can reuse elements, the logical state information of the user interface is unchanged and the previously calculated layout information can be reused, avoiding traversing the entire tree.

Conclusion:

Not all widgets are rendered independently; RenderObject objects are created only if they are inherited from RenderObjectWidget. There are three important trees in the process of Flutter rendering. The Flutter engine renders against the Render tree.

Widget tree, Element tree, Render tree

  • Each Widget creates an Element object

  • Implicitly call the createElement method, which creates three elements

  • RenderElement is used to create RenderObject objects. Widgets that inherit from RenderObjectWidget create RenderElement

    • Create RanderElement

    • Flutter calls the mount method and createRanderObject method

  • StatefulElement inherits ComponentElement, and StatefulWidget creates a StatefulElement

    • Call the createState method to create the State

    • Assign the Widget to state

      • Call state’s build method and pass itself out,The context inside the build is the Element of the Widget
  • StatelessElement inherits ComponentElement, and StatelessWidget creates StatelessElement

    • Basically, call the build method and pass itself out

    Reference article:

    zhuanlan.zhihu.com/p/135969091

    www.yuque.com/xytech/flut…