Skip to main content

What to do

Abstractly speaking, we should produce a new UI when doing a preempt. For example, when showing enter-page animation, the new UI will be a screen shifting pixel by pixel as time goes by.

More concretely, what is the new UI? This needs some background of Flutter internal implementation. The UI that the ui thread submits to rasterizer thread is indeed a Scene object, and it is submitted to rasterizer via window.render.

How to create Scene

So how can we create the latest UI inside the preempt render? Let's firstly discuss the lowest-level approach, and below we will provide a wrapper so users can create widgets easily.

Recall how Flutter is implemented. During a normal frame pipeline, the build and layout phase modifies RenderObject (and other things), while the Layer tree is untouched and is still old (i.e. has content from last frame). During the paint phase, RenderObject will modify the Layer tree by utilizing its new data. Finally, the Scene is built from the Layer tree, and submitted to rasterizer via window.render.

Recall the preempt render is called inside the build or layout phase. Therefore, during a preempt render, we have dirty RenderObject tree and should not utilize it. However, the Layer tree is, foruntately, non-dirty and ready to be used, with content generated from the plain-old rendering in the last frame.

Now consider what happens during a preempt render. For simplicity, suppose we are doing a page-enter animation, and the widget handling page shifting is bound to a specific OffsetLayer. Then, inside preempt render, we simply do something like thatOffsetLayer.offset += 10px. By doing so, the UI will have the new page shifted a bit, i.e. the animation progresses a bit. After that, we can submit the whole layer tree object to rasterizer (indeed convert to Scene and call window.render).

Thus, we now have a mechanism for 60FPS smooth animation, no matter how heavy the tree is to build/layout.

Pseudo-code is like this:

void preemptRender() {
flutterMainLayerTree.thatOffsetLayer.offset += 10px;
window.render(buildScene(flutterMainLayerTree));
}

No worries, this is not the end - see next section for how we make it developer friendly.