Dart Tutorial

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:

  1. The first lines: what went wrong.
  2. “The relevant error-causing widget was”: the file and line in your code.
  3. 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 #

MessageCauseFix
RenderFlex overflowedChildren of a row or column do not fitExpanded, Flexible, or make it scroll
Vertical viewport was given unbounded heightA list inside a columnWrap the list in Expanded
setState() called after dispose()An async result arrived after the screen closedCheck mounted first
Null check operator used on a null valueA ! on something that was nullHandle the null case
Could not find the correct ProviderReading above, or outside, the providerMove the provider up
No Material widget foundAn InkWell or TextField with no Scaffold or Material aboveAdd one
Looking up a deactivated widget’s ancestorUsing context after an awaitCheck context.mounted
A RenderBox was not laid outUsually a follow-on from an earlier errorFix 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.

ToolUse it to
Flutter InspectorSee the widget tree, select a widget on screen, inspect its size and properties
Layout ExplorerSee how a row or column distributes space, and try other settings live
PerformanceFind frames that took too long, and why
CPU ProfilerSee which functions use the time
MemoryTrack memory use and find leaks
NetworkSee every HTTP request, with headers, body and timing
LoggingView logs and framework events
App SizeSee 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 Row or Column, 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 ColoredBox to see its real size.
  • Replace a complicated widget with const Placeholder() to check whether it is the cause.
  • Print the constraints: wrap in a LayoutBuilder and debugPrint('$constraints').
  • Hot restart to rule out stale state.
  • flutter clean when 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 #

  1. Reproduce it reliably. Find the exact steps.
  2. Read the first error in the console.
  3. Make a guess, then check it with a breakpoint or the inspector. Do not change code at random.
  4. Halve the problem. Remove or replace half the widgets. Is the bug still there?
  5. 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.

Practise in the playground Updated by Santosh Adhikari