Skip to content
FlutterLearn

Beginner · Lesson 10 · 11 min read

Debugging: Reading Errors and Using DevTools

Decode Flutter's most common error messages, use breakpoints and the widget inspector, and build a repeatable approach to fixing bugs.

Updated August 1, 2026

What you will learn

  • Read a Flutter error message and find the real cause
  • Recognise and fix the most common runtime errors
  • Use breakpoints and the DevTools widget inspector
  • Log usefully without slowing down release builds

Flutter's error messages are unusually good — they often tell you exactly which widget failed and suggest a fix. The skill is knowing where to look in a wall of red text, and that skill is mostly pattern recognition.

How to read an error

Flutter frames every error the same way. Read it in this order:

  1. The first line names the exception type and the short reason. This is usually enough.
  2. "The relevant error-causing widget was" tells you which of your widgets triggered it — skip past framework frames to find this.
  3. The stack trace, read top-down, looking for the first line pointing at a file in your lib/ folder. That is where to put a breakpoint.
  4. The suggestions at the bottom. Flutter frequently names the exact fix.

The errors you will actually hit

MessageCauseFix
A RenderFlex overflowed by N pixelsA child wanted more space than its Row/Column hadWrap the child in Expanded/Flexible, or make the parent scrollable
Vertical viewport was given unbounded heightA ListView inside a ColumnWrap the ListView in Expanded, or give it a fixed height
setState() called after dispose()An async callback returned after the widget was removedif (!mounted) return; before the setState
setState() or markNeedsBuild() called during buildState mutated while buildingMove the call into a callback or initState
Null check operator used on a null valueA ! on something that was nullUse ?./??, or find why the value is missing
No Material widget foundA Material widget outside a Scaffold/MaterialWrap it in Material or Scaffold
Unable to load assetAsset missing from pubspec, or a typo in the pathDeclare it under flutter: assets:, then full restart
Incorrect use of ParentDataWidgetExpanded/Positioned used outside Flex/StackOnly use them as direct children of the right parent
Dart
import 'package:flutter/foundation.dart';

// debugPrint throttles output so Android does not drop lines
debugPrint('Loaded ${articles.length} articles');

// kDebugMode is a compile-time constant — this whole block is
// removed from release builds by tree shaking
if (kDebugMode) {
  debugPrint('Auth token: $token');
}

// Structured logging, filterable by name in DevTools
import 'dart:developer' as developer;

developer.log(
  'Fetch failed',
  name: 'api.articles',
  error: error,
  stackTrace: stackTrace,
);

Breakpoints beat print statements

  • Click the gutter next to a line in VS Code or Android Studio and run in debug mode. Execution pauses and you can inspect every variable in scope.
  • Conditional breakpoints — right-click a breakpoint and add an expression like index == 47 to stop only on the case that misbehaves.
  • Step over / step into / step out move through code one line at a time; the call stack panel shows how you arrived.
  • Add an expression to the Watch panel to see it update as you step.
Dart
// Assertions run only in debug builds — a cheap way to catch bad state early
assert(items.isNotEmpty, 'Items must not be empty when building the list');

// Pause the debugger from code, at exactly the moment you care about
import 'dart:developer';
if (article.id == suspiciousId) {
  debugger();   // execution stops here when debugging
}

DevTools

DevTools opens automatically from the URL printed by flutter run, or through your IDE's Flutter panel. The tabs you will use most as a beginner:

  • Widget Inspector — click any widget on screen and jump to the code that built it. Its layout view shows the constraints and size of every box, which makes overflow bugs obvious.
  • Debug Console — your logs, grouped and filterable.
  • Network — every HTTP request with headers, timing and response body.
  • Performance — frame times, for when scrolling feels rough.
  • Memory — object counts over time, for hunting leaks.
Dart
import 'package:flutter/rendering.dart';

void main() {
  // Outlines every render box — instant visual layout debugging
  debugPaintSizeEnabled = true;
  // Highlights tap targets
  debugPaintPointersEnabled = true;
  runApp(const MyApp());
}

A repeatable method

  1. Reproduce it reliably. A bug you cannot trigger on demand cannot be verified as fixed.
  2. Narrow it down. Comment out half the screen. Does it still happen? Repeat. This finds the culprit faster than reading code.
  3. Read the actual error, not the first line of red you see. Scroll up — the real cause is often above the noise.
  4. Form one hypothesis and test it. Changing four things at once means you will not know which fixed it.
  5. Check the obvious things: is it a hot-reload artifact? Try a hot restart. Still there? flutter clean && flutter pub get.
  6. Write a test that fails on the bug once you find it, so it cannot come back silently.

Key takeaways

  • Find "the relevant error-causing widget" line — it names your code, not the framework's.
  • Most beginner errors are one of about eight patterns; learn them and the fixes are instant.
  • Use debugPrint and kDebugMode, never raw print with sensitive values.
  • The DevTools widget inspector shows real constraints and sizes — it ends layout guesswork.

Practice

Break it on purpose

Deliberately create four errors: a Row overflow, a ListView in a Column, a setState after dispose, and a null check on a null value. For each, read the message, write down which line told you the cause, then fix it. You will recognise all four instantly afterwards.

Show hints
  • For setState-after-dispose, await a long Future then setState, and navigate away while it runs.
  • Turn on debugPaintSizeEnabled while fixing the overflow to see the boxes involved.