How does Flutter manage to draw a widget on screenšŸ“±?šŸ¤”

Daniel Herrera SƔnchez
Bancolombia Tech
Published in
8 min readJun 17, 2022

It seems like a simple question, but it is part of the advanced knowledge of Flutter, and knowing how to answer this question correctly will enhance the performance of your solutions. It will help you understand that the performance of this technology is not only due to Skia; it is also due to the tremendous internal architecture it has. In this article, i will comment on the solution to the titleĀ“s question, so letā€™s start šŸ§‘šŸ½ā€šŸ’»šŸ‘©šŸ»ā€šŸ’».

We review the basics.

We have to start our walk from the basics and end up with three trees running in parallel in Flutter šŸ¤Æ. Thatā€™s right, although, in the past, we have joked that everything in Flutter is a widget, the reality is that more background elements help us implement our solutions.

The following is a quiz-type question: What is a Widget? According to the documentation, A widget is an immutable description of part of a user interface (link).
Let me ask you, if widgets are immutable elements (they donā€™t change), then, your Flutter apps are static? šŸ¤”
On Twitter, I asked this question to see if they knew the answer:

Most of the people posted that they didnā€™t know how to explain it.

So how does Flutter achieve dynamism? For this, I must tell you about the Element and the RenderObject. As you can see in the Flutter code base, the Widget definition has the @immutable annotation, which indicates that these elements are static.

Element

ā€œAn element is an instance of a widget at a particular location in the treeā€(link). According to this definition, there is an instance of an element for each widget in the tree. Therefore it is not difficult to deduce that our applications have a tree of widgets and elements. These instances are mutable. That means that they can change and communicate with the RenderObject.

RenderObject

It controls all sizes and layouts and contains all the logic needed to draw the widget it is associated with. That is the reason why the RenderObject is very expensive to instantiate. As in the previous scenario, we can deduce that we have a RenderObject tree alongside our widget tree and our element tree.
To unify these three concepts, i have created the following illustration:

With these concepts, we can understand the entire life cycle related to the drawing of widgets in our applications. So letā€™s analyze themšŸ§.

To display a widget on the screen, a fantastic journey

Letā€™s start with a simple example. Suppose that you are building a house. What kind of people would you need to make it? To mention a few, you would need an architect to design the plans and a master builder who interprets the dreams and tell the workers what they should do.

If any definition changed on the plans, would it be necessary to destroy everything that has been built? Most likely not. In most cases, the only thing you would have to do is to modify the affected area. Following the line of this example, I ask you: are the physical plans that the master-builder has, immutable? The truth is that they are, and, at the same time, they are the ones who have the commands that will be communicated to the workers. The only way to change the blueprints would be for some new ones to arrive, right? This would make the foreman compare the new ones with the old ones and make decisions. Usually, the previous plans are discarded, and the new ones endure.

So with all of the above, we can think of widgets as blueprints, i.e., settings. Youā€™ll need a lot of workers, master builders, and blueprints on a large project, so we have a lot of these in Flutter, which we call the three trees. The foreman is the Element, and the workers are the RenderObject.

Now, letā€™s go through the step-by-step construction of an on-screen element in Flutter with this analogy in mind.

For the first example, I will use one of my favorite talks on this topic in China in 2019 (link). There we were presented with the following scenario.

When the run App method is executed

The first thing it does is to add this widget to the widget tree:

After that, the Element that will manage the life cycle and the states of the widget tree is created.

So right now, we have our widget tree and our element tree:

Then the Element creates the RenderObject :

The following section is where all the settings defined in our widget are passed to the RenderObject:

Donā€™t be surprised that some names are particular because specialized workers are like in a construction site. We have Widgets, Elements and RenderObjects focused on a solution. We now have the following:

Therefore, at this stage, the RenderParagraph is the one that will be in charge of painting the configurations established in the widget.

Now there is a significant concern. Why did Flutter go to the trouble of using three trees when all this process could be contained in one?
To understand this, we have to analyze what happens in the face of a change. To do this, letā€™s look at the second example:

In this scenario, the runApp method is eliminated twice. The first execution will create the three trees we saw, but what happens when the second instruction is executed?

The first thing is that some new configurations arrive because the widget has changed before. The following canUpdate method is executed:

That determines if there can be reuse and therefore does not recreate the Element. Given this, the Element executes the updateRenderObject instruction:

As you can see, it is very similar to creating, but what it does is to reuse the renderObject, updating only its values. This means that a new renderObject was not made either.

So, in the end, the only thing that changed was the blueprints (the configurations), not their executors, which gives an impressive power of reuse.

Letā€™s check this on our own

App- Simple Image and Text Change Example

Well, we will put what we have learned to test. If what we have analyzed is true, it is expected to be reused as much as possible. Therefore we will do the following exercise:
In a simple application, we will make it so that when the user presses the button, the image and the text will change, indicating the path in which the file is hosted. As you can see in the App, the two images have different sizes and different texts (link to the app repository).
If what we are saying is true. We hope that the id of the RenderObject remains the same for both the text and the image.
Therefore, we proceed to run the application and compare the information obtained (using the Flutter development tools, we can see the widget tree and details such as the id of the objects):

RenderObject ID for Dash Gamer image (ID = RenderImage#of905)
.RenderObject ID for Dart Side image (ID = RenderImage#of905).

As we can see, a RenderObject of the type RenderImage is generated for the image. The RenderImage has an id #0f905. This means that the two images have been drawn by the same RenderObject šŸ¤Æ. So if the reuse of elements was applied.

RenderObject id for the text (RenderParagraph #2bea)

The same thing happens in the case of the text. The RenderObject is reused even though its values ā€‹ā€‹have changed. The RenderParagraph has the same ID for both scenarios.
Imagine this ability to recycle objects in our applications, where we have widget trees and constantly modify what we see on the screen. This is one of the reasons for Flutterā€™s excellent performance.
It also indicates why it is good practice to measure the reconstruction of elements and make good use of our keys since it is the identification card of our widgets.

Conclusion

I hope that with this article you can keep in mind how does Flutter do to render the elements on the screen in the most optimal way possible. We got to know the three trees executed in our applications, and we even saw an example that demonstrates the reuse of objects. If you liked the article, please click a lot on the šŸ‘ icon. If you have a concern, please let us know in the comment box šŸ’¬.

--

--

Daniel Herrera SƔnchez
Bancolombia Tech

Flutter,Dart@GoogleDevExpertšŸ’™ ā€¢ šŸ”“Youtube Channel Weincode ā€¢šŸ‘ØšŸ»ā€šŸ’»FlutterMedellin Community Lead ā€¢ šŸ™…šŸ»ā€ā™‚ļøAngular Content creator ā€¢ Speaker ā€¢GitHub: weincoder