In Flutter internals, we learned how the Flutter Framework Framework is implemented. In this paper, we will draw a sequence diagram of the core processes to deepen our understanding of the Flutter framework.

The Binding initialization

When your App starts up, the Main () method of the Flutter Engine will be called. The first thing the main() method does is initialize a binding.

  1. The Flutter Engine framework calls the main() method,
  2. The Flutter Framework Framework calls the global method runApp (Widget), passing in our own widgets (such as homeWidgets),
  3. Execute each Binding initializer in turn, starting with GestureBinding,
  4. GestureBinding registers gesture-related callbacks,
  5. SchedulerBinding registers frame rate-related callbacks,
  6. ServicesBinding registers platform communication-related callbacks,
  7. PaintingBinding initializes the ImageCache,
  8. SemanticsBinding gets auxiliary information from the Window to initialize,
  9. RendererBinding initiates PipelineOwner, registers multiple callbacks, initializes RenderView, initializes _persistentCallbacks,
  10. WidgetsBinding initializes BuildOwner, registers multiple callbacks,
  11. The WidgetsFlutterBinding creates a widget that treats the renderView as the RenderObject and the rootWidget (such as the HomeWidget) as a child, The root element is inflate out, leaving the instance of WidgetBinding to hold.
  12. WidgetsFlutterBinding calls handleBeginFrame, and handleDrawFrame renders the first frame

At this point, the initialization of a Binding is complete. The initialization process does several things:

  1. This binding holds a rootWidget. This rootWidget inflate a rootElement that holds the rootRenderObject (renderView), Specify the widget passed in by the customer (us), such as the HomeWidget, as child. With this foundation, “Widget Tree “, Element Tree, and renderObject tree are formed when building ().
  2. Registers multiple callbacks that Engine uses to notify the Framework when it receives a hardware message, such as a touch message,
  3. Render the first frame by actively calling the render interface

Pictures updated

Ticker

Ticker is a Flutter class that is closely related to animation. The Ticker sequence diagram is the sequence diagram of animation execution and page refresh.

  1. The ticker executes start() and then scheduleTick()
  2. ScheduleTick () calls SchedulerBinding to execute scheduleFrameCallback(), then makes window execute scheduleFrame() and registers _tick as a callback.
  3. After a while, when the window executes _handleDrawFrame(), the _tick executes,
  4. _tick the _onTick() passed in when ticker initialization is performed
  5. _onTick() can perform code that changes the UI, such as panting, and then after _onTick() _tick will call scheduleTick() again, telling the Engine to notify the Ticker on the next frame so that it can continue with the animation.

State

Request page update

State is our most common class, because when we want a Widget to be able to change, we use the StatefulWidget and the corresponding State. The sequence diagram below shows its logic for refreshing the page.

  1. Perform setState (),
  2. Call the corresponding Element’s markNeedsBuild(),
  3. This element is marked as dirty, and then call buildOwner. ScheduleBuildFor (this), the element stored in buildOwner,
  4. BuildOwner calls widgetBinding scheduleFrame(),
  5. Finally call window.scheduleFrame(),

As you can see, when we use setState(), we only do these things:

  1. Mark Element as dirty and save it
  2. Request page update

Handling page updates

After setState(), we initiate a page update request, and in the next frame, the Framework is asked to provide data for rendering that frame.

  1. Window calls widgetsBinding _handleDrawFrame(),
  2. WidgetsBinding calls buildowner.buildScope (),
  3. BuildOwner holds _dirtyElements, iterates over _dirtyElements and executes element.rebuild(),
  4. Executive performRebuild element (), for statefulElement executes _state. DidChangeDependencies (), then perform super. PerformRebuild (), That’s the performRebuild() method for ComponentElement,
  5. Build (), then updateChild(_child, newWidget, slot),
  6. Now that all _dirtyElements (and their child/children) are built, the Element tree is updated,
  7. The pipelineOwner methods flushLayout, flushCompositingBits, and flushPaint are executed in the pipelineOwner process.
  8. The last call renderView.com positeFrame () will generate image data sent to the Engine,
  9. After all this is done, buildOwner executes finalizeTree to implement the changes to the Element tree that were made in the previous process.

Layout, paint, build(), and ComponnetElement are all covered in detail.

flushLayout

The task of flushLayout is to calculate the offset and size for later rendering.

  1. PipelineOwner iterates through _nodesNeedingLayout, each node is a renderObject, and executes node._LayoutwithOutreSize ().
  2. Execute performLayout() for renderObject,
  3. Renderview.performlayout () if you start rendering from the root renderObject,
  4. RenderObject usually calls Child.layout () and calculates its own size based on the size of the child.
  5. The layout() method determines whether it’s relayoutBoundary, whether it’s sizeByParent, and then executes performLayout(), and then gets its size.
  6. Eventually, each renderObject knows its own size,
  7. Parent renderObject specifies the offset of the Child renderObject, and the layout process ends.

flushPaint

The task of flushPaint is to generate the Layer Tree that the Engine uses for actual rendering in the subsequent process.

  1. PipelineOwner traversal _nodesNeedingPaint, each node is a renderObject, perform PaintingContext. RepaintCompositedChild (node), It’s executing a class method of PaintingContext, which you can think of as a global method,
  2. Then use the Layer of the renderObject to instantiate a paintingContext, call renderObject._paintwithContext,
  3. We then execute the paint() method on the renderObject,
  4. Our root renderObject (RenderView), whose paint() method calls context.paintChild,
  5. PaintChild is an instance method of PaintContext, and there are two cases,
  6. If the childRenderObject’s repaintBoundary is true and _needPaint is used, then the childRenderObject and its subclasses draw themselves. Call repaintCompositedChild, and after drawing, call appendLayer to add the drawn layer to the current layer.
  7. If the childRenderObject repaintBoundary is false, which means it can be drawn with its parentRenderObject, then draw directly into the current layer.
  8. In a renderObject subclass, override the paint method and call Context.PaintChild () to traverse the entire renderObject tree, resulting in a Layer Tree for subsequent rendering.

summary

This article, based on the source of the Flutter Framework, draws the sequence diagram of binding initialization and screen update, hoping to help readers directly and intuitively understand what happens during the initialization and screen update of our App. How our Element Tree, renderObject Tree and Layer Tree are generated.