Flutter is known for its great performance regarding UI rendering. It is quite safe to say it is the fastest hybrid mobile development framework. The question one may ask — how does it work? What makes it so fast? The core three classes that are responsible for this process are: Widget, Element and Render Object.
Widget?
Everyone who had any contact with Flutter already knows what a Widget is. For those who do not know, Widget is a basic UI element. The documentation says: „A Widget is an immutable description of a part of a user interface”. The word immutable here is very important. This basically means that any changes we want to perform on the user interface need a new Widget to be created. This sentence raises even more questions. If there is a new Widget needed every time we change something in the UI, how come it is so fast? Here the concept of trees comes into play. Under the hood, Flutter uses 3 instances of trees. Yes, you guessed it, these are: Widget tree, Element tree and Render Object tree. Each one of them has slightly different purposes, but combined together they allow for fast UI rendering and great performance.
Element?
Element, in comparison to Widget, is mutable. It means that properties inside an element can be changed without the need of creating a new one. The documentation states that the element is: „An instantiation of a Widget at a particular location in the tree”. Element is responsible for updating the UI, being a kind of connector between Widget and Render Object.
Render Object?
Render Object is the source of information for drawing the UI. Instead of looking at the tree of Widgets, Flutter looks at the tree of Render Objects. A single instance of Render Object contains all of the info about sizes, painting and layout logic of the Widget. That makes it very expensive to create, so Flutter is trying to create a new Render Object only when necessary.
Combining all trees
To understand how it all works together, consider the following code example:
So there are actually three steps that will happen when the app starts:
- Flutter will create a Widget tree from all the Widgets used above.
- For each Widget in the tree, Flutter will create an Element by calling the createElement() method on the widget. That is how the Element tree is created.
- For each Element in the tree, Flutter will create a Render Object by calling createRenderObject() on a widget, creating a Render Object tree.
Now when we press the button, the text inside it will of course change to „Hey”. Element object which has references both to the Text Widget and its Render Object will compare their runtime types. If they are different, a new Render Object will be created, with new values. However, if the runtime type is the same, there is no need to create a new Render Object, only properties will change and updateRenderObject() will get called, saving a lot of time. In our case, the second scenario will take place. Both Element and Render Object will stay the same, the only thing that will change is a text property and a new Widget will get created. This doesn’t bother us though, because Widget objects are very light and really fast to create.
Example of a first scenario could be a view like this:
Here, when a button is pressed, a child of a button will switch between Row and Column. During rendering, Element will compare runtime types of Widget and Render Object, discover that they no longer match and instantiate a new Render Object in a place of the old one.
Optimization like above lets Flutter render the user interface really fast. However, each build requires each Element in the tree to compare its Widget and Render Object, which could take quite some time for some really complex views, with dozens of Widgets. In order to avoid such situations and improve performance, we can try to extract complex build methods to separate Widget. If we manage to extract twenty Widgets from the build method and create another Widget from it, then the number of comparisons goes from twenty to one. That is quite a saving.
Bonus: In addition to comparing the runtime type of Widget and Render Object, Element is also comparing their keys. That topic will come back in future posts.
And if you need an experienced cross-platform development team that is enthusiastic about Flutter — give us a buzz! We will gladly help you with a product that will meet all your business requirements.