If we want to understand the rendering process of FLUTTER, we must know the three most important tree widgets of Flutter and the Render tree. Their relationship, and what it means to be there. In fact, the process of rendering can also be said to be the process of creating these three tree node objects.
I. The relationship between three trees
You can see that in the picture abovewidget tree
和 element tree
It’s a one-to-one correspondence, butRender tree
It doesn’t correspond to them. This is because only inherited fromRenderObjectWidget
thewidget
That will eventually be rendered to the screen. Some of the components that we use a lot likeContainer
Or is itText
This category is a combinationwidget
Because they didn’t inherit itRenderObjectWidget
.Container
The interior is actually made up ofPadding
ColoredBox
Align
And so onText
Internal actuallyRichText
.
Widget
Describe the final appearance of the View, such as how big it is, what color it is and how far it is from the edge of the screen, etcElement
One-to-one correspondence with widgets to maintain the stable shape of the tree while holdingwidget
andrenderObject
Render
Render tree objects, which ultimately provide rendering methods such asmarkNeedsLayout
performLayout
markNeedsPaint
paint
Such methods as rendering on screen, withelement
It’s not one to one.
The process of object creation
1. Widget
Object creation
Widget object creation is very simple when you call the constructor, as shown in the following code
void main() {
runApp(MyApp());
}
Copy the code
MyApp() is the Widget object created, and it’s very simple.
2. Element
Object creation
Following the source code, we can see that each Widget class has a createElement() method, which is called when the Element object is created. We found this code in the runApp() method, but I’ve omitted some of the extraneous code for the sake of understanding the process
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) { ...... element = createElement(); . element! .mount(null, null); . return element! ; }Copy the code
So createElement() is called in this method, and the root Element is created. The mount method is then called to insert the Element into the corresponding Element Tree. We then call the inflateWidget() method to create it, which calls the createElement() method of the child element, which then calls the mount method, and so on to form the Element Tree. Briefly review the process
attachToRenderTree()
The method callcreateElement()
To create arootElement
rootElement
The objectmount()
Method will berootElement
Insert into the node of the tree and callinflateWidget()
- in
inflateWidget()
In the methodThe child widgets
callcreateElement()
andmount()
Repeat the above steps untilelement tree
Has been created
Element inflateWidget(Widget newWidget, dynamic newSlot) { .... final Element newChild = newWidget.createElement(); . newChild._activateWithParent(this, newSlot); . newChild.mount(this, newSlot); . return newChild; }Copy the code
3. Render
Object creation
Like Element, the RenderObject class has an important method called createRenderObject(), which means that the time to call this method is when the RenderObject is created. As mentioned earlier, only widgets that inherit from RenderObject will eventually be rendered. Before the RenderObject object is created. Most of the widgets in flutter have their corresponding Element as shown below
So objects that really inherit from RenderObjectElement will actually call createRenderObject(). Tracing the source code, we found that the corresponding renderObject was created when the Element object was inserted into the tree node when mount was called.
void mount(Element? parent, dynamic newSlot) { super.mount(parent, newSlot); . _renderObject = widget.createRenderObject(this); . attachRenderObject(newSlot); _dirty = false; }Copy the code
3.build
Method execution flow
There is an important method in Element called markNeedsBuild() that does two main things
- tag
element
fordirty
- The current
element
Put objects globally_dirtyElements
In the
Execute the process
- when
element
After inserting into the corresponding tree node, the currentelement
Is marked asactive
State, and then executeactivate()
- in
activate()
Method to executemarkNeedsBuild()
Mark the object asdirty
- And then the system calls it
_handleBuildScheduled()
scheduleWarmUpFrame()
scheduleFrame()
window.scheduleFrame();
A series of methods, the purpose of which is to registervsync
Signal that registration is complete and wait for the next timevsync
The arrival of the - The next time
vsync
Will trigger upon arrivalWidgetsBinding.drawFrame
buildScope
在BuildScope ()
This method will get the one that was just marked asdirty
That’s the list of_dirtyElements
Work through the list one by onerebuild()
- in
rebuild()
Method to call the correspondingperformRebuild()
, and finally callWidget build() => widget.build(this);
End of the process
In the above flow, one detail is when will the next vsync come? We use a metric called Frames Per Second, which is the number of Frames Per Second that need to be transmitted, to determine if the UI is stuck. We usually measure it by 60 FPS, and if it’s below 60 it’s going to stall, and the lower it goes, the more it’s going to stall. This is because most devices refresh at 60 FPS, or 60 frames per second, so it takes about 1/60s or 0.0166s to process a frame of data. This vsync is the signal the device sends when it starts rendering the first frame. So if the device is refreshed at 60fps, ideally the next vsync signal will come in 0.0166 seconds.
4. To summarize
Now that we know when the Widget Element Render object is created and how the build() method is executed, we can thread through all the points to get the following flow
- First create
widget
Object and then inattachToRenderTree
Method to createrootElement
rootElement
Inserted into theelement tree
, and create therenderObject
Object, followed by executionmarkNeedsBuild()
methods- through
build
Method gets the widget of the next child node and executesinflateWidget()
createelement
object - perform
mount()
Method, insertelement tree
, create the correspondingrenderObject
Object, and then execute thiselement
themarkNeedsBuild()
Method, repeat the above steps until the creation of the three trees is complete.