Skip to content
Boring Doc
Boring Doc explanation Verified against Flutter 3.47.7 4 min readerrorsRenderFlex overflowedunbounded heightunbounded widthParentDataWidget

Common Flutter errors and how to fix them

Everything is a widget: build, rebuild, dispose.

Most everyday Flutter errors are the layout contract being broken: a child that is too big for a Row or Column (RenderFlex overflowed), a scrollable or text field that was given no limit (unbounded height/width), or a widget like Expanded placed under the wrong parent. The remaining classics, setState called during build and RenderBox was not laid out, are a side effect in build() and a symptom of an earlier error respectively. Read the first error in the console, give the widget a constraint, and the rest usually disappears.

  1. Layout is a negotiation: parents pass constraints down, children pick a size within them. Nearly every red-screen layout error names the side that broke the deal.
  2. 'A RenderFlex overflowed by N pixels': a Row/Column child wants more space than the flex can give. Wrap it in Expanded or Flexible, let text ellipsize, or make the content scroll.
  3. 'Vertical viewport was given unbounded height' and 'An InputDecorator ... cannot have an unbounded width': a ListView inside a Column, or a TextField inside a Row, with nothing bounding the main axis. Give it a size with Expanded or SizedBox.
  4. 'Incorrect use of ParentDataWidget': Expanded/Flexible must be direct children of Row/Column/Flex, Positioned of Stack, TableCell of Table.
  5. 'setState() or markNeedsBuild() called during build': build() must not call setState, showDialog, Navigator.push or anything that marks widgets dirty. Move it to an event handler, initState with addPostFrameCallback, or push the dialog as its own route.
  6. 'RenderBox was not laid out' and the red (debug) or grey (release) screen are symptoms; fix the first exception in the log. Customize what users see with ErrorWidget.builder, report framework errors from FlutterError.onError and everything else from PlatformDispatcher.instance.onError.
ComparisonError message, cause and fix
Error messageTypical causeFix
A RenderFlex overflowed by N pixels on the right/bottomA Row or Column child (often a long Text or an unconstrained Column) asks for more space than the flex hasWrap the child in Expanded or Flexible; set overflow: TextOverflow.ellipsis or softWrap; use Wrap or a scroll view when content can legitimately exceed the space
Vertical viewport was given unbounded heightListView, GridView or another scrollable placed in a Column (or nested in another scrollable) with no height limitWrap the scrollable in Expanded or Flexible, give it a SizedBox height, or for a short fixed list use shrinkWrap: true with NeverScrollableScrollPhysics; for scroll-in-scroll use slivers in one CustomScrollView
An InputDecorator, which is typically created by a TextField, cannot have an unbounded widthTextField or TextFormField placed directly in a RowWrap the field in Expanded or SizedBox(width: ...)
Incorrect use of ParentDataWidgetExpanded/Flexible not directly under Row/Column/Flex, Positioned outside Stack, TableCell outside TableMove the widget so its direct parent is the expected one, or remove it; a Padding or Container between them also breaks it
setState() or markNeedsBuild() called during buildsetState, showDialog, Navigator.push or a notifier change triggered synchronously inside build()Run it from an event handler, from initState via WidgetsBinding.instance.addPostFrameCallback, or push the dialog as a route when navigating to the page
RenderBox was not laid outA render object was painted or measured before layout completed, almost always because one of the constraint errors above happened firstFix the earlier error in the console; this one disappears with it
Red screen (debug/profile) or grey screen (release)An uncaught exception during build, layout or paint; ErrorWidget replaced the failed subtreeRead the first exception, add handling or constraints; override ErrorWidget.builder for a friendlier placeholder
The ScrollController is attached to multiple scroll viewsSeveral scrollables share one controller, often the PrimaryScrollController on web/desktopGive each scrollable its own ScrollController or set primary: false on the extra ones

The messages are the first line Flutter prints; the rest of each error explains which widget triggered it, so scroll up to the first assertion rather than the last.

Layout as a lease negotiation

A parent widget is a landlord offering a space with limits: at most this wide, at most this tall (sometimes 'as tall as you like'). The child is a tenant who must choose a size inside those limits and report it back. An overflow is a tenant who builds past the property line; the yellow-and-black stripes mark the trespass. An unbounded-height error is a landlord who said 'as tall as you like' to a tenant (a ListView) that only knows how to fill whatever height it is given, so it cannot answer. Expanded is the landlord assigning the tenant a definite share of the remaining space.

Where the analogy breaks: Not every widget negotiates the same way: Center and Align deliberately loosen constraints, slivers negotiate with SliverConstraints instead of boxes, and intrinsic sizing lets some parents ask the child first. setState-during-build is not a layout problem at all; it belongs to the build phase, which happens before any space is measured.

Minimal working example

A screen that avoids the two most common layout errors
dart
import 'package:flutter/material.dart';

class InboxPage extends StatelessWidget {
  const InboxPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Inbox')),
      body: Column(
        children: [
          const Padding(
            padding: EdgeInsets.all(16),
            child: Row(
              children: [
                Icon(Icons.inbox),
                SizedBox(width: 8),
                Expanded(
                  child: Text(
                    'A long subject line that would otherwise overflow the row',
                    overflow: TextOverflow.ellipsis,
                  ),
                ),
              ],
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: 50,
              itemBuilder: (context, index) => ListTile(
                title: Text('Message $index'),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
Expected: Renders a header row and a scrolling list with no overflow stripes and no assertions. Remove either Expanded and you reproduce the corresponding error from the table.Flutter ≥ 3.0.0Common Flutter errors
  1. L14A Row gives its children unbounded width on the main axis unless they are flex children.
  2. L18Expanded makes the Text a flex child: it receives the remaining width as a tight constraint, so it can never overflow the Row.
  3. L21With a bounded width the Text can wrap or, as here, truncate instead of painting past the edge.
  4. L27A Column gives children unbounded height; Expanded gives the ListView the remaining height instead, which fixes 'Vertical viewport was given unbounded height'.
  5. L28ListView.builder lays out only the visible items; it still needs a bounded height to know what 'visible' means.

Use cases

  • Yellow-and-black stripes appear at the edge of a Row or Column and the console says a RenderFlex overflowed.
  • A ListView, GridView or TextField inside a Column or Row makes the whole screen fail with 'unbounded height' or 'unbounded width'.
  • A dialog, snackbar or navigation that you trigger from build() throws 'setState() or markNeedsBuild() called during build'.
  • Users report a grey screen in release and you need to know what happened and show something better than a blank box.

Benefits

  • Each message maps to one missing constraint or one misplaced widget, so the fix is usually a single wrapper such as Expanded or SizedBox.
  • Understanding the first error avoids chasing its symptoms (RenderBox was not laid out, blank screens) one by one.
  • Centralized error handlers give you crash reports from release builds, where the red screen is never shown.

Limitations

  • These fixes assume the box layout protocol; inside a CustomScrollView the equivalents are sliver widgets (SliverList, SliverFillRemaining), not Expanded.
  • shrinkWrap: true makes a scrollable size itself to all of its children, which defeats lazy building and is slow for long lists; it is a fix for short lists only.
  • FlutterError.onError and PlatformDispatcher.onError only see errors in the main isolate; a spawned isolate needs its own error port handling.

Alternatives and trade-offs

  • Understanding constraints — You keep hitting unbounded or overflow errors and want the model behind 'constraints go down, sizes go up' rather than one fix at a time.
  • Widget tests — Layout assertions throw during pumpWidget, so a test per screen catches overflow and unbounded errors before a device ever shows them.
  • Flutter inspector and DevTools — You can see the overflow but cannot tell which widget is responsible; the inspector's layout explorer shows constraints and sizes per node.
  • State management patterns — The setState-during-build error comes from a notifier or stream firing while widgets rebuild; restructuring who owns the state removes the cause.

'A RenderFlex overflowed': a Column of text inside a Row

Problem: The Row offers unbounded width to a non-flex child; the inner Column sizes itself to its longest Text, which is wider than the screen, so the Row paints past its edge and reports the overflow.

Fix: Make the Column a flex child with Expanded (or Flexible with a flex factor) so it receives the remaining width as its constraint.

✗ Unconstrained Column in a Row
dart
Row(
  children: [
    const Icon(Icons.message),
    Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: const [
        Text('Title'),
        Text('A very long body that is wider than the screen ...'),
      ],
    ),
  ],
)
✓ Expanded gives the Column a bounded width
dart
Row(
  children: [
    const Icon(Icons.message),
    Expanded(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: const [
          Text('Title'),
          Text('A very long body that is wider than the screen ...'),
        ],
      ),
    ),
  ],
)

'Vertical viewport was given unbounded height': a ListView inside a Column

Problem: A Column does not limit its children's height and a ListView expands to fill whatever height it gets, so the viewport is asked to be infinitely tall and the assertion fires (usually followed by 'RenderBox was not laid out').

Fix: Wrap the ListView in Expanded to take the remaining height, or give it an explicit SizedBox height. For a short, fixed list that should scroll with the page, use shrinkWrap: true plus NeverScrollableScrollPhysics inside an outer scroll view.

✗ ListView with no height
dart
Column(
  children: [
    const Text('Header'),
    ListView(
      children: const [
        ListTile(title: Text('Map')),
        ListTile(title: Text('Subway')),
      ],
    ),
  ],
)
✓ Expanded bounds the list
dart
Column(
  children: [
    const Text('Header'),
    Expanded(
      child: ListView(
        children: const [
          ListTile(title: Text('Map')),
          ListTile(title: Text('Subway')),
        ],
      ),
    ),
  ],
)

'An InputDecorator ... cannot have an unbounded width': a TextField inside a Row

Problem: The decorated text field needs a finite width to lay out its border, label and content; a Row offers it unbounded width.

Fix: Wrap the field in Expanded (share the row) or SizedBox(width: ...) (fixed width).

✗ Unbounded TextField
dart
Row(children: const [TextField()])
✓ Bounded by Expanded
dart
Row(children: [Expanded(child: TextFormField())])

'setState() called during build': opening a dialog from build()

Problem: showDialog pushes a route, which marks the Navigator and Overlay dirty while the framework is still building; build can run every frame, so even if it worked it would open the dialog repeatedly.

Fix: Trigger the dialog from an event (a button) or, if it must appear on entry, schedule it after the first frame in initState, or push it as a second route right after navigating to the page.

✗ Side effect in build
dart
@override
Widget build(BuildContext context) {
  showDialog<void>(
    context: context,
    builder: (_) => const AlertDialog(title: Text('Alert')),
  );
  return const Center(child: Text('Page'));
}
✓ Scheduled after the first frame
dart
@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    if (!mounted) return;
    showDialog<void>(
      context: context,
      builder: (_) => const AlertDialog(title: Text('Alert')),
    );
  });
}

This page is Boring Doc’s independent explanation. For the complete specification use the official pages below (content © The Flutter Authors, CC BY 3.0; code samples BSD). Dates show when the explanation was last checked and when the official page last changed.

  • Common Flutter errors Flutter 3.47.7 Explanation verified against current source
    https://docs.flutter.dev/testing/common-errors
    Last checked
    Oct 21, 2026
    Source last updated
    3 weeks ago
    Last synchronized
    3 weeks ago
  • Debug Flutter apps Flutter 3.47.7 Explanation verified against current source
    https://docs.flutter.dev/testing/debugging
    Last checked
    Oct 21, 2026
    Source last updated
    3 weeks ago
    Last synchronized
    3 weeks ago
  • Handling errors in Flutter Flutter 3.47.7 Explanation verified against current source
    https://docs.flutter.dev/testing/errors
    Last checked
    Oct 21, 2026
    Source last updated
    3 weeks ago
    Last synchronized
    3 weeks ago
  • https://api.flutter.dev/flutter/foundation/FlutterError-class.html
    Last checked
    Oct 21, 2026
Was this useful?
AI explanationAI-generated · grounded in cited sources
AI explanations are not configured
Set AI_PROVIDER (for example to openai-compatible with a local Ollama or LM Studio endpoint) to enable grounded explanations. Everything else on this page works without an AI provider.