The official architecture of Flutter

Flutter’s UI system

The UI system of Flutter mainly understands three trees: Wide tree, elementTree, and renderTree.Copy the code

Widget

Widgets are immutable, and their main structure is shown in the figure above. RenderObjectWidget is renderable, while StatelessWidgets and StatefulWidgets are essentially just Widget assembly containers that do not participate in rendering themselves. The Widget's createElement method generates the corresponding Element.Copy the code

Because widgets are immutable, the entire Widget tree is rebuilt each time the Flutter page is refreshed.Copy the code

Element

Element is an abstraction of a Widget, and its main structure is shown in the figure above. When a Widget is initialized, it creates an Element with createElement, which holds the Widget and RenderObject. The following code briefly describes the relationship between Element and RenderObject:Copy the code

BuildOwner logs all elements in the current tree structure that are marked as dirty. ElementTree is updated but not rebuilt when the Flutter page is refreshed.Copy the code

RenderObject

RenderObject is the object that is used for rendering. It performs layout and paint based on the layout properties of the Widget. RenderTree is updated but not rebuilt when the FLUTTER page is refreshed.Copy the code

Threading model

Platform_taskRunner thread ui_taskRunner thread gpu_taskRunner thread io_taskRunner thread. The DART layer in Flutter is a single-threaded model, with all DART code always running on the Mutator thread of its ISOLATE. The default catch that the flutter engine starts is called RootIsolate, and the RootIsolate mutator thread is the ui_taskRunner thread above. So the DART code runs on ui_taskRunner thread except for the concurrent code that we execute through isolate.spawn (). A platform_taskRunner thread is the main thread of an Android or ios application process. On Android, it corresponds to an Android UI thread. Gpu_taskRunner thread is a thread used by FLUTTER to process rendering work. Its main objects are rasterizer, including scene synthesis, rasterization and OpengL related operations. The io_taskRunner thread is used to process asynchronous time-consuming operations such as image data codec. Every flutter engine starts with its own UI, GPU, and IO threads, but platform_taskRunner is common to all engines of the current flutter process, as the main thread of the app process is always that one.Copy the code

isolate

Dart adds a layer of ISOLATE. Dart code runs in an ISOLATE. The memory between the ISOLATES is not shared and they can only communicate with each other through ports. Dart uses the keyword async/await for asynchronous operations, which are implemented as coroutines. Dart applications rely on multiple ISOLates for concurrency. Each ISOLATE is created with a Mutator thread assigned to it to run the DART code.Copy the code

The seven stages of the ISOLATE (irreversible) : *Unknown Initial state. This is an internal state, and the external world does not acquire the isolate in this stage. *Uninitialized The status of the ISOLATE is the internal state after it is created. Initialization is complete, but the DART library has not yet been loaded and is an internal state. *LibrariesSetup initialization is complete and the Dart code is loaded. *Ready is Ready to run the DART code. *Running Dart code is Running. *Shutdown ISOLATE No longer executes the DART code and is waiting to be recycled.Copy the code

Flutter application startup process

When a flutter is initialized by application, the main function in the lib/main.dart file is executed by default. In the main function, runApp() is called to start the flutter operation.Copy the code

The call to attachToRenderTree builds three trees for the first time and updates the tree only when it is marked as dirty.Copy the code

Render and refresh mechanisms

Based on Android analysis, the refresh process of simplified version is shown as follows: The DART layer marks elements in ElementTree as dirty, notifies the engine that the page needs to be refreshed, and the engine listens for the native system's Vsync via JNI. When the next Vsync arrives, The engine notifies the DART layer to update the UI tree to synthesize scene data and remove the dirty flag. After the engine gets the scene data, it performs rasterization and frame composition, and then renders the frame data to the screen.Copy the code

The division of UI threads in the rendering process

Animate, Build, Layout, Compositing Bits, Paint, compositeFrame(Give scene data to GPU thread)Copy the code

Division of gpu threads in the rendering process

Obtain layerTree and Pipline from scene for rasterization and composition, and then submit data to GPU for rendering on screen through SKia. Rasterization is to convert the drawing instruction into the corresponding pixel data, and synthesis is to carry out the related superposition and characteristic processing of the data after rasterization of each layer. This process is called a Graphics Pipeline.Copy the code

PlatformChannel workflow parsing

Either MethodChannel or EventChannel is implemented the same at the C layer. Dart side logic in a channel is always executed on the UI Thread, while native side logic in a channel is always executed on the Platform Thread.Copy the code

Start process of the Flutter Engine

The main objects of interest: Engine, shell, DartVM, ISOLATE, window. Shell: Creates a RuntimeController object. Window: is the main medium of communication between dart and C. Each RootIsolate has a Window object. Isolate: Can be understood as the dart code runtime.Copy the code

DartVM parsing

Each process has only one instance of DartVM, which is shared by all engines within the process.Copy the code
  • Obtain vm_snapshot and isolate_snapshot data from Settings
  • Register native methods for DART calls
  • Create the VM_ISOLATE object
  • The corresponding ISOLATE is created when engine is started, and the method marked “VM: Entry-point” in the DART code is associated with Window.