Mobile Development

Common Flutter Errors and Debugging Patterns

The Debuggers Engineering Team
13 min read

TL;DR

  • RenderFlex overflow is caused by content exceeding its container - fix with SingleChildScrollView, Expanded, or Flexible
  • "setState called after dispose" happens when async operations complete after a widget is removed - always check mounted before calling setState
  • JSON parsing type errors come from untyped dynamic casting - use typed model classes with null-safe factories
  • Flutter DevTools is your best friend for identifying infinite rebuild loops and performance bottlenecks

Table of Contents

Developer debugging code with error messages on a dark terminal

RenderFlex Overflowed

This is the single most common Flutter error. You will see it at least once per week.

Error message:

A RenderFlex overflowed by 42 pixels on the bottom.
The overflowing RenderFlex has an orientation of Axis.vertical.

What causes it: A Column, Row, or Flex widget tries to display children that collectively need more space than the parent provides. The yellow-and-black stripe pattern appears over the overflow area.

The problem code:

Column(
  children: [
    Container(height: 400, color: Colors.blue),
    Container(height: 400, color: Colors.red),
    // If the screen is less than 800px tall, this overflows
  ],
)

Fix 1 - Make it scrollable:

SingleChildScrollView(
  child: Column(
    children: [
      Container(height: 400, color: Colors.blue),
      Container(height: 400, color: Colors.red),
    ],
  ),
)

Fix 2 - Use flexible sizing:

Column(
  children: [
    Expanded(child: Container(color: Colors.blue)),
    Expanded(child: Container(color: Colors.red)),
  ],
)

Diagnosis tip: Open Flutter Inspector in DevTools, click the overflowing widget, and look at its constraints. The constraints will tell you exactly how much space is available versus how much the widget is trying to use.

setState Called After Dispose

Error message:

setState() called after dispose(): _MyWidgetState#abc12
This error happens if you call setState() on a State object for a widget
that no longer appears in the widget tree.

What causes it: An async operation (API call, timer, animation) completes and calls setState(), but the user has already navigated away from the screen, disposing the widget's State object.

The problem code:

class _MyScreenState extends State<MyScreen> {
  String? data;

  @override
  void initState() {
    super.initState();
    _loadData();
  }

  Future<void> _loadData() async {
    final result = await api.fetchData(); // Takes 3 seconds
    setState(() {
      data = result; // CRASH if user navigated away during those 3 seconds
    });
  }
}

The fix:

Future<void> _loadData() async {
  final result = await api.fetchData();
  if (!mounted) return; // Check if widget is still in the tree
  setState(() {
    data = result;
  });
}

For StatefulWidget subclasses, always check mounted before calling setState() after any awaited operation. With Riverpod or Bloc, this problem largely disappears because state changes are handled outside the widget lifecycle.

Error handling code displayed on a code editor with debug overlay

Null Safety Errors and Late Keyword Misuse

Dart's null safety is powerful but creates three common pitfalls:

The null assertion crash

String? userName;
print(userName!.length); // Throws: Null check operator used on a null value

The ! operator tells Dart "I promise this is not null." If you are wrong, the app crashes. Only use ! when you have a logical guarantee that the value is not null. Otherwise use ?. or ??:

print(userName?.length ?? 0); // Safe: returns 0 if userName is null

The late initialisation error

late final String config;

void doSomething() {
  print(config); // Throws: LateInitializationError: Field 'config' has not been initialized
}

late tells Dart "I will initialise this before it is read." Use it only when you genuinely initialise the variable before any possible access. If there is any code path that reads it before initialisation, you will get a runtime crash with no compile-time warning.

The safer pattern

String? _config;
String get config => _config ?? (throw StateError('Config not initialized. Call init() first.'));

Future<void> init() async {
  _config = await loadConfig();
}

This gives you a clear error message and makes the nullable state explicit.

Type Cast Errors in JSON Parsing

Error message:

type 'int' is not a subtype of type 'String' in type cast

This happens when your API returns a number where your Dart code expects a string, or vice versa. It is extremely common because JSON parsing in Dart uses dynamic types by default.

The problem code:

final json = jsonDecode(response.body);
final name = json['name'] as String; // Crashes if 'name' is null or an int

The fix - use a typed model class:

class User {
  final String name;
  final int age;

  User({required this.name, required this.age});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      name: json['name']?.toString() ?? 'Unknown',
      age: (json['age'] as num?)?.toInt() ?? 0,
    );
  }
}

Before writing your Dart model, validate your JSON with our free JSON formatter to see the exact structure and types your API returns. Many type cast errors happen because the developer assumed a field type incorrectly.

MissingPluginException

Error message:

MissingPluginException(No implementation found for method X on channel Y)

What causes it: A native plugin's platform code is not linked to your app. This typically happens when:

  1. You added a plugin dependency but did not run flutter pub get
  2. The plugin requires additional native setup (Podfile changes for iOS, Gradle changes for Android)
  3. Hot restart does not reload native code - you need a full restart

The fix:

flutter clean
flutter pub get
cd ios && pod install && cd ..
flutter run

If the error persists after a full rebuild, check the plugin's README for platform-specific setup steps. Plugins like firebase_core, google_maps_flutter, and camera all require native configuration beyond the pubspec dependency.

Infinite Rebuild Loops

Your app's CPU spikes to 100% and DevTools shows the same widget rebuilding hundreds of times per second. This is an infinite rebuild loop.

Common cause 1 - creating objects in build():

// BAD: creates a new Future on every build, which triggers setState, which triggers build...
Widget build(BuildContext context) {
  return FutureBuilder(
    future: api.fetchData(), // New Future created every build!
    builder: (context, snapshot) => Text(snapshot.data ?? 'Loading'),
  );
}

// GOOD: create the Future once in initState
late final Future<String> _dataFuture;

@override
void initState() {
  super.initState();
  _dataFuture = api.fetchData();
}

Widget build(BuildContext context) {
  return FutureBuilder(
    future: _dataFuture,
    builder: (context, snapshot) => Text(snapshot.data ?? 'Loading'),
  );
}

Common cause 2 - setState in build:

// BAD: calling setState during build triggers another build
Widget build(BuildContext context) {
  if (someCondition) {
    setState(() => value = newValue); // Triggers infinite loop
  }
  return Text(value);
}

Use Flutter DevTools Performance view to identify which widgets are rebuilding. The rebuild count indicator turns red for widgets rebuilding too frequently.

Flutter DevTools interface showing widget rebuild performance metrics

BuildContext Across Async Gaps

Error message:

Looking up a deactivated widget's ancestor is unsafe.

What causes it: You hold a reference to BuildContext, perform an async operation, then try to use the context. If the widget was removed during the async gap, the context points to a defunct widget.

The problem:

Future<void> _handleSubmit() async {
  await api.submitForm(data);
  Navigator.of(context).pushReplacement(...); // Context might be invalid
  ScaffoldMessenger.of(context).showSnackBar(...); // Context might be invalid
}

The fix:

Future<void> _handleSubmit() async {
  await api.submitForm(data);
  if (!mounted) return;
  Navigator.of(context).pushReplacement(...);
  ScaffoldMessenger.of(context).showSnackBar(...);
}

Unhandled Exception in FutureBuilder

FutureBuilder's snapshot.error is often ignored, causing unhandled exceptions to crash the app silently.

The complete pattern:

FutureBuilder<User>(
  future: _userFuture,
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const CircularProgressIndicator();
    }
    if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    }
    if (!snapshot.hasData || snapshot.data == null) {
      return const Text('No data available');
    }
    final user = snapshot.data!;
    return Text(user.name);
  },
)

Always handle all three states: loading, error, and data. For production apps, consider replacing FutureBuilder with Riverpod's AsyncValue which enforces handling all states.

Debugging with Flutter DevTools

Flutter DevTools is your primary debugging tool. Here are the features that matter most:

  1. Widget Inspector: Shows the widget tree, constraints, and render objects. Click any widget to see its size, position, and parent constraints.
  2. Performance Overlay: Shows frame rendering times with a graph. Red bars indicate frame drops.
  3. Memory View: Shows heap usage over time. Look for steadily increasing memory use, which indicates a leak.
  4. Network Inspector: Shows HTTP requests and responses. Useful for debugging API integration issues.

Launch DevTools from the command line with flutter pub global activate devtools && flutter pub global run devtools.

For testing the APIs your Flutter app connects to, test your API endpoints with our free API tester to verify response formats and status codes before writing Flutter client code.

For professional Flutter debugging support and code review, our team at The Debuggers works with teams to identify and fix complex Flutter issues that slow down development velocity.

Related reading: If you are choosing a state management solution for your Flutter app, see our Flutter State Management comparison.

Frequently Asked Questions

How do I debug Flutter errors on a physical device?

Connect your device via USB, enable Developer Options and USB Debugging (Android) or trust the computer (iOS), then run flutter run. All debug output appears in your terminal. For wireless debugging on Android, use adb tcpip 5555 and then adb connect <device-ip>:5555. Flutter DevTools connects automatically to apps running on physical devices just as it does for emulators.

Why does my Flutter app crash without a useful error message?

Silent crashes usually happen in release mode where assertions are stripped. Run the app in debug mode (flutter run --debug) to get full error messages with stack traces. If the crash only occurs in release mode, check for issues related to code obfuscation and tree shaking, which may remove code that release mode triggers. Also check that you are not relying on assert() statements for logic that should run in release mode.

What is the most common cause of frame drops in Flutter?

The most common cause is doing expensive computation in the build() method or creating new objects (controllers, futures, streams) during build. Move object creation to initState() and cache expensive computations. Use const widgets whenever possible because Flutter can skip rebuilding const widgets entirely. The second most common cause is large images without proper caching. Use CachedNetworkImage instead of Image.network for repeated network image loads.

How do I fix "No MediaQuery widget ancestor found"?

This error means you are trying to access MediaQuery.of(context) from a context that is not wrapped in a MaterialApp, CupertinoApp, or MediaQuery widget. It commonly happens when you try to access screen dimensions in the main() function or in a widget that is above MaterialApp in the widget tree. Move the code that uses MediaQuery to a widget that is a descendant of MaterialApp.


Building a Flutter app and running into API issues?

Use our free API Request Tester to debug your backend endpoints before writing client code. Validate JSON response formats with our JSON Formatter to prevent type casting errors in your Dart models.

Need Flutter debugging help for your team? The Debuggers provides Flutter development and support services for teams at any stage of their project.

Need Help Implementing This in a Real Project?

Our team supports end-to-end development for web and mobile software, from architecture to launch.

flutter errors debuggingflutter common errorsflutter debugflutter setState errorflutter overflowflutter renderFlex

Found this helpful?

Join thousands of developers using our tools to write better code, faster.