Skip to content

How to debug the performance of your Dart code using Flutter DevTools ?


Created Jan 06, 2022 – Last Updated Jan 06, 2022

DartDebugging
Digital Garden

By default, everything runs on the UI thread in Flutter.

You have to be very carefull on what you put on this thread since it can drastically affect your UX !

In this article, you will learn how to debug in depth critical piece of code using Flutter DevTools.

Let’s consider the example application, which executeSomeCode when clicking on the FloatingActionButton in order to compute a random number between 1 and 100 and then display it.

lib/main.dart
dart
import 'dart:developer';
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _nextRandomNumber = 0;
void _executeSomeCode() {
_doSomething();
_doSomethingElse();
}
void _doSomething() {
int count = 0;
for (int i = 1; i <= 1000000000; i++) {
count += i;
}
return;
}
void _doSomethingElse() {
setState(() {
_nextRandomNumber = Random().nextInt(100);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'The random number is: $_nextRandomNumber',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_executeSomeCode();
},
tooltip: 'Next random number',
child: const Icon(Icons.arrow_right_outlined),
),
);
}
}

If we run our app using flutter run and click on the button we quickly notice that it takes quite a long time to see the next random number.

Our code has a heavy computational method (_doSomething) that blocks our UI thread.

On a production code, you may not have identified it so easily and since by default, your functions does not appear in the Timeline of the Flutter DevTools, you’ll

To make them explicitly visible for profiling purpose, you can surround the suspicious methods by Timeline.startSync("_mySuspiciousFunction"); and Timeline.finishSync(); as follows:

lib/main.dart
dart
onPressed: () {
Timeline.startSync("_executeSomeCode");
_executeSomeCode();
Timeline.finishSync();
}

Then, we can run our app in profile mode using flutter run --profile.

If you open the DevTools using the link provided by the output of your terminal you should see in your browser the Flutter DevTools Dashboard as follows:

Flutter DevTools Dashboard

Now, in the app, if you execute the code by clicking the button and refresh the timeline in the DevTools (1) you should be able to find your function _executeSomeCode (2) in the Timeline (3)!

Flutter DevTools Dashboard

As you can see, this function takes 990ms to execute !

This execution time is the sum of all the children function of _executeSomeCode.

You can easily see it in the DevTools if you click on the slot in the timeline (3), it will display the execution details in the bottom panel where you’ll be able to find the tab Bottom Up (4) displaying the execution time of _doSomething (5)!

As you can see, _doSomething represents 100% of the execution time of _executeSomeCode.

In a production environment you would have much more functions with more distributed execution times but still, this view will help you to figure out what is CPU-consuming in your Flutter applications.

I hope it helped you 🤘 !


Want to learn more? Browse my Digital Garden