“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”

List fluency optimization

This is a general fluency optimization scheme that optimizes scenes with delays caused by builds, such as page transitions or complex lists that scroll quickly, through frame-by-frame rendering.

In this code, example runs on VIVO X23 (Snapdragon 660), optimized with the same scrolling operation before and after 200 frame capture data index comparison (the screen is recorded at the end of the article) :

Before optimization The optimized

Monitor tool from: Fps_monitor, metrics details: Page fluency is no longer a mystery! Debug artifact out of the box, Flutter FPS detection tool

  • Smooth: a frame takes less than 18ms
  • Good: a frame takes between 18ms-33ms
  • Slight lag: a frame takes between 33ms-67ms
  • Catton: A frame takes more than 66.7ms

After adopting frame splitting optimization, the number of lag times decreased from an average of 33.3 frames to only one frame in 200 frames, and the peak value also decreased from 188ms to 90ms. The lag phenomenon is greatly reduced, the ratio of smooth frames is significantly improved, and the overall performance is more smooth. Here are the details.

Before optimization The optimized
The average number of frames with a frame lag 33.3 200
The average number of frames with a slight lag 8.6 66.7
Most time consuming 188.0 ms 90.0 ms
The average time 27.0 ms 19.4 ms
Fluid frame ratio 40% 64.5%

Page switching is smoother

When a page or Tab is opened, the system renders the entire page and combines animation to complete the page switch. For complex pages, frames will also lag. With the frame-by-frame component, the build of the page is broken down frame by frame and viewed through the performance tools in DevTools. The peak value of the switching process was reduced from 112.5ms to 30.2ms, and the overall switching process was smoother.


Collection of practical optimizations: Collection of Keframe Fluency Optimization practices

How to use it?

Project dependency:

Add a keFrame dependency in pubspec.yaml

dependencies:
  keframe: version
Copy the code

Components only distinguish between non-empty security and empty security versions

Non-empty safe use: 1.0.2

The empty security version uses 2.0.2

Github: github.com/LianjiaTech…

Pub check: pub. Dev /packages/ke…

Dont forget star ~

Quick start:

See the figure below

Suppose now the page is composed of four parts: A, B, C and D, each part takes 10ms, which is 40ms when the page is built. Each part is nested using the FrameSeparateWidget. During page construction, simple placeholders will be rendered in the first frame, and A, B, C and D will be rendered separately in the next four frames.

For lists, nest the FrameSeparateWidget within each item and the ListView within the SizeCacheWidget.


Constructor description

FrameSeparateWidget: A frame-divider component that renders nested widgets in a single frame

type Parameter names If required meaning
Key key no
int index no Frame component ID,Scenarios using SizecacheWidgets are mandatory, the Size information corresponding to the index is maintained in the SizeCacheWidget
Widget child is The widgets that actually need to be rendered
Widget placeHolder no Placeholder widget, try to set simple placeholder, do not pass default is Container()

SizeCacheWidget: Caches the size information of the actual widget nested by the frame-splitting component in the child node

type Parameter names If required meaning
Key key no
Widget child is Cache if the child node contains a frame componentThe actual widget size
int estimateCount no Estimate the number of child nodes on the screen to improve response time during fast scrolling

Scheme design and analysis:

The nature of Cattons is that single frames take too long to draw. Based on this, two solutions are naturally derived:

1. Reduce the drawing time of one frame, because there are many reasons that lead to long time, such as unreasonable refresh or long drawing time, which need to be analyzed on a case-by-case basis. Later, I will share some of my optimization experience.

2. Without time optimization, the task of one frame is divided into multiple frames to ensure that each frame does not time out. This is also the design idea of this component, rendering in frames.

As shown in the figure below:

The principle is not complicated, the question is how to put this mechanism into practice in Flutter.

Because it involves scheduling frames and systems, it is natural to see if there is an API in the SchedulerBinding.

The scheduleTask method was discovered, which is a method provided by the system to perform tasks, but this method has two problems:

  • 1. The render task is heap-sorted by priority, and heap-sorted is unstable, which causes the task to be executed in a non-FIFO order. In effect, the list will not be rendered in order, but will be rendered in a bouncing manner

  • 2. This method itself has scheduling problems. I have submitted the issue and PR, but I have been stuck in unit testing.

Fix: the Tasks scheduled through ‘SchedulerBinding. Instance. ScheduleTask’… # 82781

Finally, with reference to this design and the use of endOfFrame method, the frame queue is completed. The entire rendering process looks like the following:

For the list-building scenario, assume that five items can be displayed on the screen. First, in the first frame, the list renders the five placeholder widgets and adds five high-priority tasks to the queue, either by simply replacing the placeholder Widget with the actual item or by animating it with a gradient. The placeholder Widget is replaced with the actual list item in the next five frames.

Double the ListView fluency!! The Flutter Caton analysis and general Optimization scheme are analyzed in more detail in this article.


Check out some examplesGithub)

Caton’s pages are often rendered by multiple complex widgets at the same time. By nesting the FrameSeparateWidget for complex widgets. When rendering, the frame-splitting component renders multiple Palceholders simultaneously in the first frame, followed by complex subitems in successive frames to improve the smoothness of the page.

For example, the pre-optimized example in example:

ListView.builder(
              itemCount: childCount,
              itemBuilder: (c, i) => CellWidget(
                color: i % 2= =0 ? Colors.red : Colors.blue,
                index: i,
              ),
            )
Copy the code

The CellWidget has a height of 60 and three TextField components nested inside (the overall build time is around 9ms).

The optimization is simply to nest frame components for each item and set the placeHolder (style) as simple as possible to match the actual item.

In the list case, set the ListView to a nested SizeCacheWidget, and it is recommended to set the preload cacheExtent to a larger size, such as 500 (the default is 250), to improve the slow slideout experience.

In addition, you can also nest transparency/displacement animations for items to optimize the visual effect.

The effect is as follows:


Cost of framing

Of course, the frame scheme is not perfect, in my opinion, there are two main costs:

1. Additional build overhead: The build cost of the entire build process changed from “n * Widget cost” to “N * (Widget + placeholder) cost + system scheduling N frames cost”. As you can see, the extra overhead is mainly due to the complexity of the placeholder. If the placeholder is just a simple Container, the test shows that the overall build time increases by about 15%. This extra cost is almost negligible on today’s mobile devices.

2. Visual changes: As shown above, the component renders the item in frames and visually occupies the page to become an actual widget. However, since the list has a cache area (it is recommended to increase the cache area), the user will not be aware of it in the high-end machine or normal scrolling situation. A quick swipe on a mid – to low-end device can feel the transition, but it’s better than a heavy drop.


Demo before and after optimization

Note: GIF frame rate is only 20

Before optimization The optimized

Finally: A little thought

This is the end of list optimization. There are two things that I feel deeply about throughout the open source practice:

The relationship between “point” and “surface”

When we think about technical solutions, we can go from “point” to “surface” and think about the essence of the problem from a higher view.

In the execution of the time, it is necessary to step by step split from “surface” to “point”, grasp the key nodes of the problem, and draw up a schedule, and gradually crack.

In many cases, this logic of thinking up and down is our core competence.

Meet all changes with constancy

When it comes to the unknown, we tend to overcomplicate it. At the beginning of the analysis of the list building principle, I was also struggling to start, and took a lot of detours. However, the core of the UI framework of Flutter is still the mechanism of building three trees.

In this system, seize the invariable things, whether it is life cycle, routing and so on questions can be found from inside. I have previously summarized the Core Rendering mechanism of Flutter and the Design of Flutter routes and source code parsing.

In the next stage, I will focus on the I/O part of Dart and analyze and practice it from the shallow to the deep, combining with computer network principles. Starting from the underlying principles, we will learn the “unchanging principles” together and make progress together. If you have any questions, you can contact me through the official account. If the article inspires you, I hope to get your likes, attention and collection, which is the biggest motivation for me to keep writing. Thanks~

If you have any questions or suggestions, please feel free to contact me in the comment section or the official account, or issue or PR.

Public account: Advance Flutter or Runflutter collects the most detailed guide to advance and optimization of Flutter, welcome to follow.