Flutter Lesson 69 of 83 5 min read
Debugging Flutter Apps with DevTools
Find and fix bugs in Flutter: read error messages, use the widget inspector, breakpoints, logging and DevTools, and handle errors in release.
On this page
Bugs are normal. What matters is finding them quickly. Flutter gives you detailed error messages and one of the best sets of debugging tools of any framework.
Read the error #
When a widget throws while building, debug mode shows a red screen and prints a long report. It looks intimidating and is mostly helpful. Look for three things:
- The first lines: what went wrong.
- “The relevant error-causing widget was”: the file and line in your code.
- The suggestion: Flutter often says exactly how to fix it.
A RenderFlex overflowed by 42 pixels on the right.
The relevant error-causing widget was:
Row file:///.../lib/order_tile.dart:18:14
Consider applying a flex factor (e.g. using an Expanded widget) to force
the children of the RenderFlex to fit within the available space...
Errors you will meet #
| Message | Cause | Fix |
|---|---|---|
| RenderFlex overflowed | Children of a row or column do not fit | Expanded, Flexible, or make it scroll |
| Vertical viewport was given unbounded height | A list inside a column | Wrap the list in Expanded |
| setState() called after dispose() | An async result arrived after the screen closed | Check mounted first |
| Null check operator used on a null value | A ! on something that was null | Handle the null case |
| Could not find the correct Provider | Reading above, or outside, the provider | Move the provider up |
| No Material widget found | An InkWell or TextField with no Scaffold or Material above | Add one |
| Looking up a deactivated widget’s ancestor | Using context after an await | Check context.mounted |
| A RenderBox was not laid out | Usually a follow-on from an earlier error | Fix the first error in the log |
Always fix the first error. The ones after it are often consequences.
Logging #
debugPrint('Loaded ${items.length} items');
Prefer debugPrint to print. It avoids dropped lines on Android. For anything beyond quick checks, use log from dart:developer or the logging package, as in the Dart lesson on logging.
Code that should only run during development:
import 'package:flutter/foundation.dart';
if (kDebugMode) {
debugPrint('Token: $token');
}
assert(() {
debugPrint('This runs only in debug mode');
return true;
}());
kDebugMode, kProfileMode and kReleaseMode are constants, so the compiler removes the dead branch from release builds.
Breakpoints #
Click beside a line number in VS Code or Android Studio and run with the debugger (F5). Execution pauses there, and you can inspect every variable, step line by line and evaluate expressions. The Dart lesson on debugging explains stepping and conditional breakpoints.
Tick “All Exceptions” or “Uncaught Exceptions” in the breakpoints panel to pause at the exact line that throws.
Flutter DevTools #
Open it from your editor (the command Flutter: Open DevTools in VS Code, or the DevTools button in Android Studio) while the app is running.
| Tool | Use it to |
|---|---|
| Flutter Inspector | See the widget tree, select a widget on screen, inspect its size and properties |
| Layout Explorer | See how a row or column distributes space, and try other settings live |
| Performance | Find frames that took too long, and why |
| CPU Profiler | See which functions use the time |
| Memory | Track memory use and find leaks |
| Network | See every HTTP request, with headers, body and timing |
| Logging | View logs and framework events |
| App Size | See what makes the build large |
The widget inspector #
This is the tool you will use most.
- Select widget mode: tap anything on the device, and the inspector jumps to that widget in the tree and in your code.
- Layout Explorer: for a
RoworColumn, shows each child’s size and flex, and why something overflowed. - Show guidelines: draws outlines, padding and alignment over the running app.
- Highlight repaints: shows which areas are redrawing, to spot unnecessary work.
- Slow animations: runs animations at one fifth speed, so you can see what they really do.
You can toggle guidelines from code as well:
import 'package:flutter/rendering.dart';
void main() {
debugPaintSizeEnabled = true; // outlines around every widget
runApp(const MyApp());
}
Quick tricks #
- Wrap a widget in a brightly coloured
ColoredBoxto see its real size. - Replace a complicated widget with
const Placeholder()to check whether it is the cause. - Print the constraints: wrap in a
LayoutBuilderanddebugPrint('$constraints'). - Hot restart to rule out stale state.
flutter cleanwhen a build error makes no sense.
Errors in a released app #
In release mode there is no red screen. A failing widget shows a grey box and the error is lost unless you catch it.
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
// Errors thrown by the Flutter framework (build, layout, paint).
FlutterError.onError = (details) {
FlutterError.presentError(details);
reportError(details.exception, details.stack);
};
// Errors outside Flutter's callbacks, such as in async code.
PlatformDispatcher.instance.onError = (error, stack) {
reportError(error, stack);
return true;
};
runApp(const MyApp());
}
void reportError(Object error, StackTrace? stack) {
// Send to a crash reporting service.
debugPrint('Reported: $error');
}
Use a crash reporting service such as Firebase Crashlytics or Sentry. Both provide these handlers and show you which errors affect the most users, with stack traces.
You can also replace the grey box with something friendlier:
ErrorWidget.builder = (details) => const Center(
child: Text('Something went wrong on this screen.'),
);
A method for hard bugs #
- Reproduce it reliably. Find the exact steps.
- Read the first error in the console.
- Make a guess, then check it with a breakpoint or the inspector. Do not change code at random.
- Halve the problem. Remove or replace half the widgets. Is the bug still there?
- Fix it and write a test so it cannot return.
Try it yourself #
Create a screen with three deliberate bugs: a Row that overflows, a list inside a column without Expanded, and a setState after an awaited delay on a screen you have closed. Find each one using only the error text and the widget inspector, then fix it.