How does Flutter use Widgets, Elements and RenderObjects to achieve such stunning visuals?
This paper has been translated into Chinese with The permission of The author. In view of my English ability and expression ability is limited, please English level enough friends to the original address to read =. =.
Widget
Widget
Widget
Widget
padding
Widget
recognise gestures
Widget
Widget
Widget
The Four Layers
You may already have a general understanding of Flutter in articles like “Introduction to Flutter”. But you don’t really understand what these layers represent. Maybe you looked at this picture for 20 seconds like I did and didn’t know what to make of it. Don’t worry. I’ll help you. Look at the picture on the bottom.
Material
Cupertino
Rendering
Rendering
dart:ui
dart:ui
Engine
1. The dart:ui library
Dart: THE UI Library exposes the lowest level of services that are used to bootstrap the Application, such as driving input, text drawing, layout, and rendering subsystems.
So you can build a Flutter App just by instantiating classes in the DART: UI library (such as Canvas, Paint, and TextField). But if you’re familiar with drawing directly on a canvas, you know that drawing a pattern using these low-level apis can be difficult and tedious. Next, consider something that isn’t drawing, such as layout and hit tests. What does all this mean? This means you have to manually calculate all the coordinates used in your layout. Then mix some drawing and hit tests to capture the user’s input. Do the above for each frame and track them. This works well for simple apps, such as one that displays text in a blue area. If for those more complex apps or simple games can be enough for you to suffer. Not to mention animations, scrolling, and some cool UI effects that product managers love most. I can tell you from my years of development experience that these are endless nightmares for developers.
2. The Rendering library
Flutter Rendering tree. The RenderObject hierarchy is used by the Flutter Widgets library to implement its layout and background rendering. In general, although you may use the RenderBox to implement custom effects in your application, in most cases the only interaction we have with the RenderObject is when debugging layout information.
Dart: UI Library is the first abstraction layer on dart: UI Library. It does all the heavy math for you (like keeping track of coordinates that need to be computed constantly). It uses RenderObjects to handle this work. You can think of RenderObjects as a car engine that does all the work of bringing your APP to the screen. All RenderObjects in the Rendering tree are layered and drawn by Flutter. To optimize this complex process, Flutter uses an intelligent algorithm to cache objects that are expensive to instantiate to achieve optimal performance. In most cases, you will find that Flutter uses RenderBox instead of RenderObject. This is because the project’s builders found it possible to successfully build an effective and stable UI using a simple and box-layout constraint. Imagine all the widgets are placed in their boxes. The parameters in this box are calculated and then placed among the other boxes that have been arranged. So if only one Widget changes in your layout, the box in which it was loaded is recalculated by the system.
3. The Widget library
Flutter Widgets framework
The Widget library is perhaps the most interesting. It is another abstraction layer used to provide widgets out of the box. All the widgets in this library belong to one of three categories of widgets handled with the appropriate RenderObject.
Layout
For example,Column
andRow
Widgets are used to help us easily work with the layout of other Widgets.Painting
For example,Text
andImage
Widgets allow us to display (draw) some content on the screen.Hit-Testing
For example,GestureDetector
Allows us to recognize different gestures, such as clicking and swiping.
Most of the time we use a few “base” widgets to make up the widgets we need. For example, we use GestureDetec to wrap the Container, and the Container wraps the Button to handle Button clicks. This is called composition, not inheritance. However, in addition to building each UI component itself, the Flutter team also created two libraries containing the commonly used Material and Cupertino style Widgets.
4. The Material & Cupertino library
The Library of Widgets using Material and Cupertino design specifications.
Flutter created this layer of Widgets in the Material and Cupertino styles to reduce the burden on developers.
Put it all Together
How do RenderObjects connect to Widgets? How does Flutter create a layout? What is Element? Enough said, let’s learn by doing. Consider the following Widgets tree.
Stateless Widget
SimpleApp
SimpleContainer
SimpleText
runApp()
- Flutter builds the Widgets tree that contains these three Widgets.
- Flutter traverses the Widget tree and is called based on the widgets in it
createElement()
To create the corresponding Element objects, and then assemble them intoElement
The tree. - A third tree is created that contains the values corresponding to the Widget
Element
throughcreateRenderObject()
RenderObject created. Here is the state of Flutter after it has gone through these three steps:
Flutter creates three different trees, one for Widget, one for Element, and one for RenderObject. Each Element has a reference to the corresponding Widget and RenderObject.
So what is a RenderObject? RenderObject contains all the logic used to render instance widgets. It is responsible for layout, painting, and hit-testing. It is performance-intensive to generate, so we should cache it as much as possible. We keep them in memory for as long as possible, and even recycle them (because their instantiation is really expensive). That’s where Element comes in. Element is the bridge between the mutable Widget tree and the immutable RenderObject tree. Element is good at comparing two objects, namely the Widget and the RenderObject, in a Flutter. It configures the Widget’s position in the tree and keeps references to the corresponding RenderObject and Widget. Why use three trees instead of one? In short, it’s for performance. When the Widget tree changes, Flutter uses the Element tree to compare the new Widget tree with the original RenderObject tree. The RenderObject needs to be recreated if the Widget and RenderObject types in one location are different. If the widgets in other locations are of the same type as the RenderObject, you only need to change the configuration of the RenderObject instead of performing performance-costly RenderObject instantiation. Because widgets are lightweight and require very little performance to instantiate, they are the best tool for describing the state of your APP (that is, configuration). Heavyweight RenderObjects (which are expensive to create) need to be created as little as possible and reused as much as possible. As Simon said: The whole Flutter APP is like a RecycleView. However, in the framework, elements are removed, so you don’t have to deal with them very often. The context passed in the Build (BuildContext Context) method of each Widget is the Element that implements the BuildContext interface, which is why individual widgets of the same category differ.
Computer the Next Frame
Because widgets are immutable, the entire Widget tree needs to be rebuilt whenever a Widget’s configuration changes. For example, when we change the color of a Container to red, the framework triggers an action to rebuild the entire Widget tree. Then, with the help of Element, Flutter compares the type of the first Widget in the new Widget tree with the type of the first RenderObject in the RenderObject tree. The type of the second Widget in the Widget tree is then compared to the second RenderObject in the RenderObject tree, and so on until the Widget tree is compared to the RendObject tree.
Determine newWidget
And the oldWidget
Is it the same type
Widget
Element
RenderObject
RenderObject
SimpleApp Widget
SimpleAppRender
SimpleContainer Widget
RenderObject
SimpleObject
SimpleContainerRender
SimpleContainerRender
Widgets
RenderObject
Widget
Widget
RenderObject
RenderObject
SimpleButton
Element
Element
RenderObject
Element
SimpleTextRender
SimpleButton
Element
RenderObject
RenderObject
Conclusion
You should now have a general idea why Flutter can render complex layouts so quickly. I hope this article has helped you better understand the design philosophy inside Flutter. I’m Frederik Schweiger on Twitter and LOOK forward to talking to you.