Mastering Dart & Flutter DevTools — Part 8: Performance View

Flutter Gems
10 min readDec 28, 2022

by Ashita Prasad (LinkedIn, Twitter), fluttergems.dev

This is the eighth and final article in the “Mastering Dart & Flutter DevTools” series of in-depth articles. In this article, you will learn how to analyze the perceived performance of a Flutter app using the DevTools’ Performance View tool. In case you want to check out any other article in this series, just click on the link provided below:

Installation and setup of DevTools is a pre-requisite for this part. In case you missed it, the installation and setup details are provided in detail here.

More than 5,000 apps are released across app stores everyday! Which means, getting downloads is a challenging task, but retaining users is an even tougher battle. App performance is the key to retaining users as more that 80% people delete or uninstall an app because of performance issues. Users love fast & smooth UI (high perceived performance) and hate slow & choppy “janks”. These janks can manifest in a variety of ways, such as slow loading times, stuttering animations, or other types of performance issues that make the app feel unresponsive or difficult to use. In general, an app is considered to be janking if it doesn’t perform as smoothly as expected or desired.

It is important to remove janks from a mobile app because janky performance can be frustrating for users and can make an app difficult or unpleasant to use. These apps have a low perceived performance and can feel slow and unresponsive, which can lead to a poor user experience and potentially even cause users to stop using the app altogether. Janky performance is also considered as a sign of underlying technical issues or inefficiencies in the app, which can impact its reliability and overall quality.

In this article, we will see how DevTools’ Performance View can help us detect whether the frames are janking while using an app. We will also look into some strategies that can help remove these janks.

Performance View

The Performance View tool is a part of DevTools that helps gather timing and performance information while performing any app activity.

To better understand the usage of Performance View, let us directly get started with the exercise.

Step 1: Getting the Source Code

Create a local copy of the below repository 👇

Open the repo in GitHub as shown below.

Click on the green button <> Code, then visit the Local tab and click Download ZIP to download the source code for this exercise.

Extract the file using any unzip tool.

We now have a local copy of the source in the folder performance_view_demo-master.

Step 2: Opening the Project in VS Code

Launch VS Code, open the project folder in VS Code and go to pubspec.yaml file.

Click on Get Packages to fetch all the packages required for the project.

Click Get Packages

Step 3: Launching Performance View in VS Code

For Performance Profiling, the app must be run on an actual device for accurate results. So you must connect an iOS or Android device. In case of a desktop app, if your development desktop OS is also the target platform, you can also compile and run the app in profile mode on the desktop.

After connecting the device (with USB debugging mode turned on), click on No Device in the status bar and select the target device. If a default device is already selected, you can click on it and change the target device.

Select Target Device

In this exercise, as we are building and running the app on an actual android device, let us go ahead and select the target device.

Physical Android Device selected as Target

Now, run the app (main function) in Profile mode as shown in the image below.

Click “Profile” to run the app in Profile mode

Click on Dart DevTools in the status bar and then click on the Open DevTools in Web Browser command.

Click Dart DevTools
Open DevTools in Web Browser

Now, click on the Performance tab.

You can check out the entire process in the image below.

Launching Performance View
App Home Screen

As soon as we open the Performance View, we can see “Shader compilation jank detected” error as shown in the image below.

ERROR: Shader compilation jank detected

This jank happens on the first app run and is currently being addressed in the latest rendering runtime for Flutter — Impeller. You can read more about it here. As this issue will get resolved in near future, we will not cover it in this study.

Anatomy of Performance View

Now, let us go through the interface of Performance View. The window consists of 4 parts:

1. Toolbar

Performance View Toolbar

The Performance View toolbar provides us with options to Pause Profiling, Resume Profiling and Stop Profiling.

An option is also provided to add a Performance Overlay directly on the app. Let us go ahead and press it.

On enabling the Performance Overlay, frame statistics for the last 300 frames along with aggregates (max. rendering time, avg. rendering time) are directly displayed on the app as shown below.

Performance Overlay Enabled

The upper graph is for the time spent in the Raster thread (scene rendering) and the lower graph is for the time spent in the UI thread (Dart code execution) while building a frame. The vertical green bars represent the current frame.

Later in this exercise we will see how these graphs can help us quantitatively detect janks in the UI.

The toolbar also contains more options that can enhance the level of tracking.

2. Flutter Frames Chart

Flutter Frames Chart

This chart presents the application’s frame information, with each set of coloured bars representing a single Flutter frame. In order to avoid any janking, a frame should be created and displayed within 1/60th of a second (~16ms). If a frame exceeds this limit, the bar is coloured in red to indicate a janked frame. If the UI frame time bar is red, it means that the Dart code is too expensive. In case the Raster frame time bar is red, it implies that the scene (graphics) is too complicated to render quickly.

3. Timeline Events Chart

The Timeline Events chart provides a deeper analysis of each frame. When a bar is selected in the Frames Chart, the corresponding timeline details are highlighted in this flame chart.

Timeline Events Chart

This chart shows all events that went into building the frame both in UI thread and the Raster thread.

4. CPU Profiler Details

When an event is selected in the Timeline events chart, its entire CPU profiling information is presented in this window.

To learn more about CPU Profiling and how you can analyze these details, do check out our article on CPU Profiler View.

Step 4: Running the Performance View tool

Now that we are familiar with the Performance View interface, let us proceed to run the following two scenarios:

  • Scenario #1 — In the first scenario, we will rapidly scroll down a screen and check whether there is any jank detected using the Performance View tool. Then we will proceed to identify the cause of this janking.
  • Scenario #2 — In the second scenario, we will modify the scenario #1 code and again analyze the performance information to ensure that the app is providing a jank free experience.

Scenario #1

Let us launch the 1st scenario by clicking on the Scenario #1 button.

To test whether there is any janking, rapidly scroll down the page using your fingers as shown in the video (Link) below.

Scenario #1: Exercise

As you can observe, the user experience is not good while performing this app activity on the device due to janking. Also you can see that the Performance Overlay on the app shows a red bar in the Raster plot as the instructions are taking > 16 ms in this thread.

The corresponding performance data is also captured in Performance View as shown in the image below. It can be observed that there is significant janking in almost every frame rendered in the Flutter Frame chart.

Scenario #1: Performance View

Let us inspect the code to understand what might be the cause of this poor performance.

// screens/shrinklists.dart

class _WrappedListsPageState extends State<WrappedListsPage> {

...
@override
void initState() {
...
innerLists.add(
ListView.builder(
itemCount: numberOfItemsPerList,
itemBuilder: (BuildContext context, int index) => innerList[index],
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Scenario #1"),
),
body: ListView.builder(
itemCount: numLists,
itemBuilder: (context, index) => innerLists[index]),
);
}
}

We can see that the code renders a nested ListView, with inner ListView widgets having their shrinkWrap value set to true. This shrinkWrap property is forcing the evaluation of height of the inner ListView widget by building all the widgets in that ListView even if the widgets are outside the screen. This can have a significant impact our app’s performance.

Let us go ahead and rectify the code in Scenario #2.

Scenario #2

In Scenario #2, we have now replaced the shrink wrapped ListView widgets with SliverList that are built dynamically whenever the user scrolls.


class _SliversPageState extends State<SliversPage> {
...

@override
void initState() {
...
innerLists.add(
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) => innerList[index],
childCount: numberOfItemsPerList,
),
),
);
...
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Scenario #2"),
),
body: CustomScrollView(slivers: innerLists),
);
}

Let us now launch the 2nd scenario by clicking on the Scenario #2 button.

Scroll to generate the performance profile as shown in the video (Link) below.

Scenario #2: Exercise

The app scrolling experience improved significantly this time. You can also observe vertical green bars in both the plots of the Performance Overlay for a significant period of scrolling which indicates no existence of janked frames.

The corresponding performance data is also captured in the Performance View as shown below. You can witness how janking has been significantly reduced in the Flutter Frame chart.

Thus, we saw it first-hand how DevTools’ Performance View helped us detect performance issues in our app that was subsequently fixed to provide a better user experience.

In this article, we walked through the process of detecting janks by analyzing the data using DevTools’ Performance View. In the first scenario, we investigated and identified the cause of low performance using the tool. This problem was subsequently resolved in Scenario #2 by using SliverList instead of shrink-wrapped ListView. This fix was then verified using the Performance View and we first-hand witnessed how the app experience was significantly improved.

We would love to hear your experience with the DevTools’ Performance View or about any other performance issue you faced in your app. In case you faced any issues while going through this exercise or while running the tool for your project, please feel free to mention it in the comments and we can definitely take a look into it. Also, in case you have any other suggestion, do add it in the comments.

In the previous articles of this series, we have discussed other tools available in the DevTools suite that can help you build high-performance Flutter apps. Don’t forget to check out the links below to navigate to the tool you want to learn next:

Source Code(s) used in this article:

Special thanks to Kamal Shree (GDE — Dart & Flutter) for reviewing this article.

--

--

Flutter Gems

Maintained by Ashita Prasad, Flutter Gems is a curated package guide for Flutter ecosystem. Visit https://fluttergems.dev