Mastering Dart & Flutter DevTools — Part 2: Flutter Inspector
by Ashita Prasad (LinkedIn, Twitter), fluttergems.dev
This is the second article in the “Mastering Dart & Flutter DevTools” series of in-depth articles. In this article, you will learn about DevTools’ Flutter Inspector via an example app case study. In case you want to check out any other article in this series, just click on the link provided below:
- Part 1: Introduction & Installation
- Part 2: Flutter Inspector [You are Here]
- Part 3: App Size Tool
- Part 4: Network View
- Part 5: Logging View
- Part 6: CPU Profiler View
- Part 7: Memory View
- Part 8: Performance View
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.
As mobile apps are growing at a rapid pace (~6k apps released per day on Apple and Google Play Store combined), there is a fierce competition to grab user’s attention and retention. The user interface (UI) of an app plays a crucial role in the overall user experience that is important for retaining users. A well-designed UI can make an app more intuitive and easy to use, while a poorly designed UI can make even the most powerful app difficult and frustrating to use. A good UI can also help differentiate your app from others on the market and make it more appealing to users. Thus, a well-designed UI is essential for the success of any app.
In Flutter, you can design and build beautiful User Interface (UI) through the use of widgets. To create a layout in Flutter, you can use a combination of these widgets to create the structure and appearance of your app’s user interface. But, what happens when your app’s layout has issues and the UI does not match the intended design?
This is where Flutter Inspector comes to our rescue. It can be used to understand existing layouts as well as diagnose any layout issues that may exist in an app. And the best part is that the visual debugging of the Layout can be performed on-the-fly.
The objective of this article is to walk you through the Flutter Inspector tool that can be used to debug any layout issues and understand it better using a real-life example app.
Flutter Inspector
Flutter Inspector is a tool that can be used for inspecting the widget tree of a Flutter app. Using this tool, you can easily visualize and inspect the layout, size, and visual properties of the widgets that compose the app’s UI. This tool can also be used to identify performance issues, perform visual debugging of the app and provide detailed information about the widgets.
To better understand how you can leverage the Flutter Inspector tool, let us go through a case-study exercise. In this exercise, we will begin with the UI layout in the left, as shown in the image below. Our objective is to identify the issues in the UI and debug them visually using the Flutter Inspector. After resolving all the UI issues visually, we will make the necessary code changes and transform the layout into the desired UI (right screen).
Now, let us get started with the case study.
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 flutter_inspector_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.
Step 3: Launching Flutter Inspector in VS Code
Before we launch the Flutter Inspector, we must run the app.
Click on No Device
in the status bar and select a target device. If a default device is already selected, you can click on it and change the target device.
We will go ahead and select an android emulator as the target device.
Now, click Run
to run the app (main function) as shown in the image below.
Flutter Inspector can be launched using the following instructions:
Click Dart DevTools in status bar >
Click Open DevTools in Web Browser > Click Flutter Inspector tab
Anatomy of the Flutter Inspector
The Flutter Inspector window has three major parts:
- Toolbar
- Widget Tree Pane
- Layout Explorer / Widget Details Tree Pane
Let us quickly walk through these parts:
1. Flutter Inspector’s Toolbar
Flutter Inspector’s toolbar provides features that help in debugging layout issues visually. To activate any feature you just need to press the corresponding button in the toolbar. The available features are:
- Select Widget Mode — Widgets can be selected in the interactive Widget Tree Panel to inspect their properties. But, this can be cumbersome in case the widget tree is deep. Enabling this mode allows the user to select a widget directly on the app’s UI, so that it can be further inspected.
- Slow animations — When slow animations is enabled, all animations runs 5 times slower allowing for easy visual inspection. It is useful for observing and tweaking any animation that might not look fully right.
- Show guidelines — When this feature is enabled, guidelines are drawn in the app displaying render boxes, alignments, paddings, scroll views, clipping and spacers. This tool can be used to better understand and modify the layout such as better aligning the widgets or removing any unwanted padding.
- Show baselines — This tool is useful for visually inspecting the alignment of texts using baselines that are horizontal lines used for positioning texts. Selecting this option will make all the baselines visible, allowing you to precisely check the vertical alignment of the text.
- Highlight repaints — This tool is useful for highlighting the widgets that are repainting frequently, that can hamper the performance of the app. Every time a box repaints, a border is drawn around it in a rotating rainbow colour.
- Highlight oversized images — As the name suggests, this tool highlights high resolution images that are using too much memory by inverting colours and flipping them. Fixing these images is important as they can cause poor performance, especially on lower end devices and in case you have many images on the screen like in a GridView or a ListView.
2. Widget Tree Pane
In Flutter, any piece of UI that is rendered on the screen is a widget. The layout of a Flutter app’s user interface is composed of a hierarchy of widgets that is better known as the widget tree. The tree consists of individual widgets, each of which has its own properties and child widgets. This hierarchical structure is the primary reason why Flutter is able to efficiently compose and render the user interface. The Widget Tree pane is useful for viewing this widget tree and it can be used to select any widget in the tree to further inspect its layout and properties.
3. Layout Explorer / Widget Details Tree Pane
Whenever a flex widget (Row
, Column
, Flex
) or any child of a flex widget is selected in the Widget Tree Pane, its layout can be visualized in the Layout Explorer (shown above). This view also provides various tools using which we can visually change the existing layout. To get a more detailed view of all the properties and values of the selected widget, you can click on the Widget Details Tree Tab as shown below.
After getting familiar with the interface (anatomy) of the Flutter Inspector Page, let us now proceed to the case study.
Case Study
Our objective is to use the Flutter Inspector to visually debug layout issues in the example app so that we can get our desired UI.
Looking at the two UIs, we can identify 4 major problems in the layout:
- Layout Problem #1 — Over-flowing text in the first row.
- Layout Problem #2 — Emojis are not well-spaced and are not at the center of the remaining available vertical space in the page.
- Layout Problem #3 — The horizontal divider is missing.
- Layout Problem #4 — Submit button is not center aligned.
Let us go ahead and use the Flutter Inspector to fix these layout problems one at a time.
Layout Problem #1
The first issue that we need to resolve in our UI is the overflowing text.
On launching the Flutter Inspector, we can see Error 1/1
in the Widget Tree Panel.
Click on the Down Button as shown in the image below to navigate to the error causing widget. The error encountered is ‘A RenderFlex overflowed by...’
error, which is one of the most commonly occurring error. You can see yellow & black stripes indicating the area of overflow both in the app UI and the Layout Explorer.
As shown in the image below, the reason for this error is due to the fact that the child widget Text
is not constrained in its size and is trying to be wider (by 237 pixels
) than the width permitted by its parent (Row
).
As shown in the image below, we can debug this issue directly from the Layout Explorer by setting the flex
of the Text
widget as 1. This way we are telling the Text
widget that it can expand only till it fills the available space along the main axis of the Row
widget.
Voila! The issue is now fixed.
But, hold on a second. If you go ahead and restart the app, the issue comes back again to haunt us.
This is because visually fixing any issue using the Layout Explore is not permanent as no code changes are being made. It is a visual debugging tool using which you can play around and identify the correct solution. After arriving at the solution, you now have to perform the necessary code changes to persist the solution.
Let us go ahead and modify the existing Problem #1 code given below
// Problem #1
Row(
children: const [
Text(
'Please provide your valuable feedback by sharing your experience with us!',
style: TextStyle(
fontSize: 18.0,
),
),
],
),
by wrapping the Text
widget inside the Expanded
widget so that widget fills the entire available space horizontally. The default value of flex
of the Expanded
widget is 1
which is same as the value of flex
we had set using the Layout Explorer.
The modified code is given below:
// Problem #1
Row(
children: const [
Expanded(
child: Text(
'Please provide your valuable feedback by sharing your experience with us!',
style: TextStyle(
fontSize: 18.0,
),
),
),
],
),
After modifying the code, the new changes should automatically Hot Reload. In case it doesn’t, then press Restart to propagate the changes we made.
After fixing the first layout problem our app UI looks as follows.
Layout Problem #2
The next problem we need to fix is the layout of the emojis.
As shown in the image above, the emojis should be at the center of the remaining available vertical space. Also, the emojis should be evenly spaced.
Before we proceed further, let us go ahead and enable the Show Guidelines
mode from the toolbar as shown in the image below. These guidelines will help us better debug the layout visually.
Let us start with creating some even spacing between the emojis.
As shown in the image above, click on the Row
widget in the widget tree that contains the Emoji
widgets. In the Layout Explorer, the widgets inside the Row
are currently aligned as start
along the main axis. Click on the dropdown and select the alignment as spaceEvenly
. Now you can observe that the Emoji
widgets are evenly spaced just as we wanted them to be.
Next, we want to expand this Row
containing emojis to occupy the entire remaining vertical space.
As visible in Widget Tree Pane in the image above, the Row
containing Emoji
widgets is the second child of the Column
. Go ahead and click on the parent Column
widget and scroll down to locate this Row
in the layout explorer. Now, set the flex
of this Row
as 1
. As the parent of this Row
is a Column
, its main axis is along the vertical direction. So setting the flex
as 1
tells this Row
that it can expand vertically as long as the Column
permits.
Although we were able to expand the emoji row vertically, still the emojis are not in the center as desired. This is because each Emoji
widget is a Column
containing an emoji Text
and a label Text
as its children. When the row expands vertically, the columns do too, as it can be seen via guidelines. But, these children are aligned towards the start of the main axis of the Column
widget, so their position does not change. As shown in the image below, we can change their alignment via the Layout Explorer. Click on the Column
widget inside each Emoji
widget and change the main axis alignment to center
.
As this is the required solution, let us go ahead and make the necessary code changes.
class Emoji extends StatelessWidget {
....
@override
Widget build(BuildContext context) {
return Column(
// Add this line to align Emoji & Text to center
mainAxisAlignment: MainAxisAlignment.center,
children: [
...
],
);
}
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
...
//Problem #2
// Wrap the Row widget inside an expanded widget to
// expand vertically
Expanded(
child: Row(
// Add spaceEvenly main axis alignment inside Row
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: const [
Emoji('😀', 'Good'),
Emoji('😐', 'Average'),
Emoji('🙁', 'Bad'),
],
),
),
...
}
}
The 3 code changes to be made are:
- The
Row
ofEmoji
widgets is wrapped inside theExpanded
widget so that it can occupy the entire remaining vertical space. mainAxisAlignment: MainAxisAlignment.spaceEvenly
added to the above row to space theEmoji
widgets evenly in the horizontal direction.mainAxisAlignment: MainAxisAlignment.center
added to theColumn
widget of theEmoji
to center align its children vertically.
After resolving the Layout Problem #2, the UI is now heading towards our desired UI.
Layout Problem #3
The next task is to figure out the curious case of the missing horizontal divider.
On inspecting the widget tree as shown below, we can see that although the Divider
widget is present, still it is not visible in the UI. If we click on the widget and look at the Layout Explorer, its width is computed as 0.0
as no width constraint is provided.
Go ahead and set the value of flex
as 1
so that the width constraint is supplied and it expands to occupy the entire available width of the Row
.
Let us make the necessary code changes by wrapping the Divider
inside an Expanded
widget as shown below:
// Problem #3
Row(
children: const [
// Wrap the Divider inside the Expanded widget
// to provide width constrain
Expanded(
child: Divider(
color: Colors.black,
),
),
],
),
After solving Layout Problem #3 we obtain the following UI.
Layout Problem #4
The final task remaining at hand is to center align the Submit
button.
This issue can be quickly fixed by changing the main axis alignment of the children inside the last Row
widget. Click on the widget and select center
from the main axis alignment dropdown as shown below.
Let us go ahead and perform the corresponding code changes.
// Problem #4
Row(
// Add mainAxisAlignment as center
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
debugPrint('Submit button pressed.');
},
child: const Text(
'Submit',
),
),
],
),
The final UI after solving the Problem Layout #4 is shown below.
Yay! We have successfully obtained the desired UI Layout with the help of Flutter Inspector tool that helped us in debugging the issues visually.
In this article we took the help of DevTools’ Flutter Inspector tool to visually debug a UI Layout containing various problems. We started with a given Layout and identified various layout issues like overflowing text, uneven spacing between emojis, missing horizontal divider and submit button not being center aligned. Using the tools provided inside Flutter Inspector, we resolved those issues, made the necessary code changes and finally obtained the desired UI.
We would love to hear your experience with the Flutter Inspector. 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 remaining 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:
- Part 1: Introduction & Installation
- Part 3: App Size Tool
- Part 4: Network View
- Part 5: Logging View
- Part 6: CPU Profiler View
- Part 7: Memory View
- Part 8: Performance View
Source Code(s) used in this article:
Special thanks to Kamal Shree (GDE — Dart & Flutter) for reviewing this article.