This article should be the end as well as the beginning.

Introduction:

As a Flutter developer, we simply combine widgets to enable a variety of interactions. How does Flutter perform on-screen rendering through widgets? What role does the Native layer play? As an epilogue to the core UI rendering process, we don’t get bogged down in every detail this time, but rather take a holistic look at the main process and grasp the key points. For the details, I will attach some more in-depth articles. Hopefully this will help you understand the Flutter rendering mechanism.


SetState ()

Whether it’s a simple list or a clever animation, it’s essentially a series of fast-moving frames, called “frames.” When we’re scrolling through a list, or playing an animation. The system continuously produces frames and draws them onto the screen to give the impression of “moving”.

SetState () is the first thing that comes to mind when updating the UI presentation in Flutter. Updating the UI is essentially replacing the previous frame with a new frame. Therefore, frame scheduling logic must be performed. SetState final call to BuildOwner scheduleBuildFor.

  /// dart
  /// Adds an element to the dirty elements list so that it will be rebuilt
  /// when [WidgetsBinding.drawFrame] calls [buildScope].
  void scheduleBuildFor(Element element) {
    .......
    if(! _scheduledFlushDirtyElements && onBuildScheduled ! =null) {
      _scheduledFlushDirtyElements = true;
      onBuildScheduled();
    }
    _dirtyElements.add(element);
    element._inDirtyList = true; . }Copy the code

There are two key points in the method:

1, onBuildScheduled ()

2. Add element to _dirtyElements

There’s nothing to say about the second point, which will come later, but let’s focus on the first point. Tracking reference, will find that the first method will execute to SchedulerBinding. ScheduleFrame (), which is the source of drawing.


Second, rendering origin: SchedulerBinding. ScheduleFrame ()

  /// dart
  /// Call C++ to Native layer and request Vsync signal
  void scheduleFrame() {
    if(_hasScheduledFrame || ! _framesEnabled)return;
     ensureFrameCallbacksRegistered();
     window.scheduleFrame();
    _hasScheduledFrame = true;
  }
Copy the code

This method doesn’t have much code, but the key is window.scheduleFrame(), which is a native method.

  void scheduleFrame() native 'Window_scheduleFrame';
Copy the code

In android, for example, will eventually perform to registered JNI_OnLoad AsyncWaitForVsyncDelegate Java interfaces. The asyncWaitForVsync, this interface in the Flutter startup initialization. The implementation is as follows

new FlutterJNI.AsyncWaitForVsyncDelegate() {
        @Override
        public void asyncWaitForVsync(long cookie) {
          Choreographer.getInstance()
              .postFrameCallback(
                  new Choreographer.FrameCallback() {
                    @Override
                    public void doFrame(long frameTimeNanos) {
                      float fps = windowManager.getDefaultDisplay().getRefreshRate();
                      long refreshPeriodNanos = (long) (1000000000.0/ fps); FlutterJNI.nativeOnVsync( frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie); }}); }}Copy the code

Initialization can be seen: Insight into Flutter engine startup

Choreographer. GetInstance (). PostFrameCallback listens for vertical sync signal, vertical signal comes next callback doFrame, through FlutterJNI. NativeOnVsync walked to the c + +. After a complex link, add the following tasks to the event queue in UI Task Runner:

lib/ui/window/platform_configuration.cc

void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) {...// Call _window.onbeginFrame in dart
  tonic::LogIfError(
      tonic::DartInvoke(begin_frame_.Get(), {Dart_NewInteger(microseconds),}));
  / / microTask execution
  UIDartState::Current() - >FlushMicrotasksNow(a);// Call _window.ondrawframe in dart
  tonic::LogIfError(tonic::DartInvokeVoid(draw_frame_.Get()));
}
Copy the code

The initialization process of the Flutter render mechanism can be seen as follows: UI thread (this article is based on the SDK version earlier, some function positions have been changed)

This approach has three main flows:

1. Execute window.onBeginFrame, which maps to handleBeginFrame() in dart.

2. Then execute the MicroTask queue (so microtasks are not only scheduled after an Event is executed)

OnDrawFrame (); dart ();

2 is well understood as executing MicroTask. Next we will focus on 1 and 3.

Summary: the process is initiated by Dart, and a C++ callback to the Native layer registers a vsync listener. Wait until the signal arrives and tell Dart to render. It can be seen that Flutter rendering is first initiated by the Dart side rather than passively waiting for vertical signal notification. This can be explained by, for example, some static pages where the entire screen is not rendered multiple times. And because it is the vSYNC signal of Native layer, it is completely suitable for high-brush devices.


Iii. Flutter scheduling life cycle

Analyzing handleBeginFrame and drawFrame is not complicated and can be understood from the Flutter scheduling state.

Enumerations defining five scheduling states in SchedulerBinding (in order of precedence) :

Enumerated values instructions
SchedulerPhase. transientCallbacks HandleBeginFrame is triggered and executed_transientCallbacksTasks in a collection. As the name suggests,_transientCallbacksIs a temporary collection, usually filled with animated tasks via a Ticker.
SchedulerPhase. midFrameMicrotasks This state is reversed in the handleBeginFrame, but the actual task is scheduled by C++ for MicroTask (2 above).
SchedulerPhase. persistentCallbacks HandleDrawFrame fires and executes_persistentCallbacksTasks in a collection. These tasks are required for a frame, such as Build, Layout, paint.
SchedulerPhase. postFrameCallbacks HandleDrawFrame is triggered and executed after the previous task set has completed_postFrameCallbacksTasks in a collection. Because the system has been laid out, you can get the size information of the widget (RenderBox, RenderSliver) at this stage.
SchedulerPhase.idle HandleDrawFrame fires, and all tasks are completed and in the idle phase.

There are five states that a frame can go through, and we’re going to combine handleBeginFrame and drawFrame.

HandleBeginFrame: Handles tasks before rendering

This method deals mainly with temporary tasks, such as animation. For example, when we animate a widget to zoom in, we essentially calculate and draw the corresponding size state for each vertical signal as it comes in. Some pages may have animations, some may not. So this is a “temporary” set of tasks. The corresponding schedulerPhase.TransientCallbacks and schedulerPhase.midFramemicrotasks states are reversed in the lifecycle.

The process of the Flutter AnimationController callback can be seen here

DrawFrame: Core rendering flow

Here is the core process of rendering, as can be seen from the source code comments, a frame of rendering has 10 steps:

Among them, 1 and 2 have been mentioned in handleBeginFrame. Among the remaining steps, there are three main steps closely associated with rendering:

  • Build: Remember when setState() added element to the dirty collection? At this stage, Flutter updates the Element and RenderObject trees of all the nodes in the dirty collection (which need to be updated) via widgets.

Build phase details: so I’ve been using setState() incorrectly? And what should I say when the interviewer asks me about the life cycle of State

  • Layout: The RenderObject tree makes Layout measurements that determine the size and location of each display element.

Details of Layout stage: After summarizing 30 examples, I understood the Layout principle of Flutter and spent two days analyzing 15 examples to thoroughly master the Layout principle of Flutter

  • Paint: The Paint phase triggers the rendering of the RenderObject and generates a fourth Tree, the Layer Tree, which is finally rasterized and rendered.

Paint stage details: Zhang Fengjiatelei, Jiatelei, god of drawing, recommended to buy a booklet.


conclusion

Key points:

1, scheduleFrame callback C++ registration Vsync signal

OnBeginFrame; window.onDrawFrame

3. In the drawFrame phase, element and renderObject trees are generated based on widgets in the Build phase; Layout measures the size and position of drawing elements. Layer tree is generated in paint phase.

Understand the significance and role of each state in the scheduling life cycle

The whole process is as follows:


The last

This is the last article in the UI mechanics column, but as I said at the beginning, I think this is both the last and the first. Don’t get bogged down in details. Learn the whole idea first. I think it is a better way to learn.

Ps: Writing is not easy, click like coin ~

The list fluency optimization frame component is about to complete the review process and is expected to be released within next week. Welcome to follow me.

Past quality columns:

How to design and implement a high-performance Flutter list

The Flutter core rendering mechanism

Flutter routing design and source code analysis

Flutter event distribution

The most detailed guide to the advancement and optimization of Flutter has been collected in Advance of Flutter or RunFlutter.