All widgets

Widget Rendering process

Flutter abstracts the organization and rendering of view data into three parts: Widgets, Elements, and RenderObjects.

Widget

Widgets are the basic logical unit of a spatial implementation that stores configuration information about view rendering, including layout, rendering properties, event response information, and so on.

Page rendering follows the “Simple is best” philosophy. Widgets are designed to be immutable, so when the configuration information of the view rendering changes, Flutter chooses to rebuild the Widget tree for data updates, which is simple and efficient in a data-driven UI build.

The downside is that it stresses garbage collection because it involves destroying and rebuilding a large number of objects. However, the Widget itself doesn’t involve actually rendering the bitmap, so it’s just a lightweight data structure that costs very little to rebuild.

In addition, due to the immutable rows of widgets, rendering nodes can be reused at a lower cost, so there may be different widgets for the same rendering node in a real rendering tree, which undoubtedly reduces the cost of rebuilding the UI.

Element

An Element is an instantiated object of the Widget that hosts the context data for the view build and is the bridge that connects the structured configuration information to the final rendering.

The rendering process of Flutter can be divided into three steps:

  • First, the corresponding Element tree is generated from the Widget tree.
  • Then, create the corresponding RenderObject and associate it with the Element. RenderObject property;
  • Finally, the RenderObject tree is built to complete the final render.

The Element holds both the Widget and the RenderObject. Neither the Widget nor the Element is responsible for the final render, only for the RenderObject.

Why not just work with the Widget command RenderObject instead of adding the Element tree?

Widget direct commands greatly increase the performance cost of rendering.

Because widgets are immutable, but Elements are immutable. In fact, the Element tree layer abstracts the changes in the Widget tree (similar to the React virtual DOM Diff) so that only the parts that really need to be modified can be synchronized to the real RenderObject tree, minimizing the modifications to the real rendering view and improving the rendering efficiency. Instead of destroying the entire render view tree for reconstruction.

RenderObject

RenderObject is the object that is primarily responsible for rendering the view.

Rendering object tree the rendering process of Flutter is divided into four stages, namely layout, drawing, composition and rendering. While layout and drawing are done in RenderObject, Flutter uses a depth-first mechanism to traverse the tree of rendered objects, determine the position and size of each object in the tree, and draw them on different layers. Once the painting is done, compositing and rendering is left to Skia.

By introducing the three concepts of Widget, Element and RenderObject, Flutter makes the complex building process from view data to view rendering simpler and more straightforward, ensuring high rendering efficiency while being easy to centralized governance.

RenderObjectWidget introduction

In Flutter, the layout and drawing work is actually done within RenderObjectWidget, another subclass of widgets. RenderObjectWidget RenderObjectWidget

abstract class RenderObjectWidget extends Widget { @override RenderObjectElement createElement(); @protected RenderObject createRenderObject(BuildContext context); @protected void updateRenderObjct(BuildContext context, covariant RenderObject renderObjct); . }Copy the code
RenderObjectWidget is an abstract class. This class has methods for creating an Element, creating a RenderObject, and updating a RenderObject. In fact, RenderObjectWidget itself is not responsible for creating or updating these objects.

For Element creation, the Flutter calls createElement to synchronize the Widget’s configuration as it traverses the Widget tree, generating Element objects for the corresponding nodes. RenderObject is created and updated in the RenderObjectElement class.

abstract class RenderObjectElement extends Element {
  RenderObject _renderObject;
  
  @override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    _renderObject = widget.createRenderObject(this);
    attachRenderObject(newSlot);
    _dirty = false;
  }
  
  @override
  void update(covariant RenderObjectWidget newWidget) {
    super.update(newWidget);
    widget.updateRenderObject(this, renderObject);
    _dirty = false; }... }Copy the code
After the Element is created, the Flutter calls the Element’s Mount method. In this method, the associated RenderObject is created and inserted into the render tree. The Element inserted into the render tree is displayed on the screen.

If the Widget’s configuration data changes, the Element node that holds the Widget is marked as dirty. When the next cycle is drawn, the Flutter triggers an update of the Element tree and updates itself and the associated RenderObject object with the latest Widget data, which then goes into the Layout and Paint process. The actual drawing and layout process is completely done by RenderObject:

abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
  ...
  void layout(Constraints constraints, {bool parentUsesSize = false{...}) } void paint(PaintingContext context, Offset offset){} }Copy the code
With the layout and drawing complete, it’s up to Skia to do the rest. The Bitmap is synthesized directly from the rendering tree during VSync signal synchronization and then submitted to the GPU.