RenderFlex Overflow: Fix the Yellow Stripe Fast
Wrap the child in Expanded or scroll it: RenderFlex overflow means a Row or Column got less room than its children need.
20+ years shipping production backend systems. Notes here come from systems that actually shipped.
- ✓Flutter SDK installed with a runnable sample app
- ✓Basic Row, Column, and widget-test knowledge
- ✓One small-screen device or simulator profile
- RenderFlex overflow means a Row or Column got less space than its children demand, so Flutter paints the yellow-and-black stripe where pixels spill out
- Inside a Row, wrap the greedy child in Expanded or Flexible so it claims only leftover space instead of infinite width
- Inside a Column, bound scrolling lists with Expanded or wrap the whole column in SingleChildScrollView when content can exceed the screen
- Use LayoutBuilder to read real constraints and switch layouts at breakpoints instead of hard-coding widths that break on small phones
Picture packing a moving box that is exactly one foot wide. You lay three items side by side that add up to fourteen inches, so two inches stick out past the cardboard. Flutter does the same thing on screen: a Row is a box with a fixed width, and when its children are wider than that box, the extra pixels poke out. Instead of silently clipping them, Flutter paints a yellow-and-black striped warning right where the spill happens.
You run the app on your own phone and the checkout screen looks perfect. Then a tester opens it on a smaller device and the pay button slides halfway off the edge under a yellow-and-black striped bar. That stripe is Flutter's RenderFlex overflow warning, and it is one of the most reported layout errors in production apps. It never crashes the app, which is exactly why it ships: everything works on the developer's large phone, so nobody notices until users with small screens or large fonts see broken UI.
The root confusion is Flutter's constraint model. Unlike the web, where content can push a container taller, Flutter sends constraints down the tree and sizes flow back up. A Row tells each child its maximum width, then asks how big each child wants to be. When the answers add up to more than the Row owns, something has to give — and by default Flutter refuses to guess, painting the stripe instead.
This guide shows you how to read that stripe like a diagnostic. You will learn why Expanded and Flexible exist, when a Column needs a scroll view instead of flex, how LayoutBuilder exposes the real constraints at runtime, and why nesting a ListView inside a Column explodes without a bound. By the end you will fix overflow in minutes and write layouts that hold up on 320-pixel phones, tablets, and 200-percent font scales.
Reading the Stripe: What Overflowed by 60 Pixels Really Means
The console message is a precise diagnostic once you learn its grammar. A RenderFlex overflowed by 60 pixels on the right tells you four things: the guilty widget is a Row-like flex, the combined children exceed the available width by exactly 60 logical pixels, the spill runs off the trailing edge, and the fix must reclaim 60 pixels or add scrolling. On the bottom means a Column ran out of vertical room. Treat the pixel count as a budget: you need to shave or scroll at least that much.
The stripe itself is a development-only paint. Flutter draws diagonal yellow-and-black bars over the spilled region so testers can photograph it, but release builds hide the paint while keeping the broken layout. That is why overflow ships so often — developers see a clean release build on a big phone and assume all is well. Train your team to treat any stripe in a screenshot as a release blocker, not cosmetic noise.
Start every investigation by reproducing at the smallest width you support. Open the screen, read the exact pixel count, and divide the blame among children: fixed-width widgets are innocent, while unbounded Text and image children are the usual suspects. Once you know the deficit and the greediest child, the fix in the next sections becomes mechanical rather than guesswork.
Fixing Row Overflow With Expanded and Flexible
A Row gives each child its ideal width in order, and overflow means the ideals sum past the budget. Expanded solves this by replacing one child's ideal with a computed share: after fixed children are measured, the remaining space is split among Expanded children by flex factor, and each Expanded child must fit exactly that share. Wrap the stretchiest child — usually the label — and the Row always balances, on any screen width.
Flexible with the default loose fit is the gentler option: the child may occupy up to its share but can shrink smaller, which suits badges and chips that should hug their content. Use Flexible with Tight fit only when you truly need Expanded-like behavior under a different name. A common error is wrapping every child in Expanded, which just moves the fight inside equal shares and still clips long text — pick one shock absorber per Row.
Pair the flex child with an overflow strategy on text. Expanded alone forces the Text into a smaller box, but without overflow or maxLines the text still tries to paint past its box and you get a second, subtler clip. Set overflow to TextOverflow.ellipsis and a sensible maxLines so the label degrades gracefully instead of bleeding under its neighbor.
Fixing Column Overflow: Scrolling Beats Squeezing
Column overflow runs along the vertical axis: a form with six fields fits on a tall phone but spills past the bottom on a short one, or under the keyboard when it opens. Squeezing with Expanded helps only when every child can shrink — text fields and buttons have minimum heights, so beyond a point there is nothing left to squeeze. Scrolling is the honest fix: it admits the content is taller than the viewport and gives the user a way to reach it.
Wrap the Column in a SingleChildScrollView when the content is finite and each child keeps a natural height — forms, detail pages, and settings screens. For long or infinite lists, prefer ListView, which builds children lazily instead of inflating hundreds of rows at once. Never nest a ListView inside a SingleChildScrollView without a bound; two scrolling widgets fighting over one axis produce the unbounded-height error covered later.
Watch the keyboard. A Column that fits perfectly will overflow the moment the keyboard steals 300 pixels. Setting resizeToAvoidBottomInset to true (the default) shrinks the scaffold body, and combined with a scroll view the focused field scrolls into view. Test every form twice: once idle, once with the keyboard open and the first field focused.
LayoutBuilder: Branching on Real Constraints
Hard-coded widths are guesses, and guesses break. A 200-pixel card side fits a 390-pixel phone in portrait but overflows the same phone in split-screen or a 320-pixel device. LayoutBuilder ends the guessing by handing your builder the exact BoxConstraints the parent dealt — read constraints.maxWidth at build time and choose the layout that fits reality, not the device you happen to own.
The standard pattern is a breakpoint branch: below 360 pixels render a compact stacked layout, above it render the roomy side-by-side Row. Because the branch reads constraints rather than screen size, it also adapts inside dialogs, drawers, and split views where the screen is wide but your widget is narrow. MediaQuery answers how big the screen is; LayoutBuilder answers how big your box is — and your box is what overflows.
Keep branches cheap and few. Two layouts (compact and regular) cover nearly every phone; add a third wide branch only for tablets and desktop. Share child widgets between branches so state and controllers survive the switch, and test each branch by pumping the widget inside a SizedBox of the target width rather than owning five physical devices. Log the chosen branch in debug builds so QA screenshots tell you which layout rendered.
Unbounded Height: ListView Inside a Column
This sibling error reads Vertical viewport was given unbounded height and it shares the same root cause as overflow: a Column hands its children infinite max height, and a ListView — which wants to be infinitely tall — happily accepts, then cannot build a finite frame. Flutter throws instead of painting because no finite layout exists. If you see this message next to a stripe, fix the bound first; the stripe often vanishes with it.
The standard fix is wrapping the ListView in Expanded, which converts the leftover column space into a hard height bound. The list scrolls inside exactly the remaining room while headers and footers keep fixed sizes. Use this whenever the list should fill available space — chat threads, cart item lists, and search results with a persistent action bar.
Reach for shrinkWrap only for short, finite lists where the column itself scrolls or fits: three settings rows under a header. Shrink wrap forces the list to measure every child up front, destroying lazy building — fine for five rows, deadly for five thousand. And never place an Expanded ListView inside a SingleChildScrollView: the scroll view offers infinite height, Expanded demands a finite one, and you are back to the same exception.
Proving the Fix on Every Screen Size
A fix you tested on one phone is a rumor. Overflow depends on width, locale length, and text scale multiplying together, so prove the fix across that matrix before merging. Pump the screen in widget tests at 320, 390, and 600 logical pixels with your longest locale strings, and assert tester.takeException returns null at every size. That single assertion catches both the stripe and the unbounded-height throw in one line.
Add text scaling to the matrix. Wrap the pumped widget in MediaQuery with a 2.0x scaler and re-run — labels that fit at default size frequently burst at accessibility sizes, and app-store review teams test exactly that. If the design cannot fit at 2.0x, that is a product decision to make deliberately, with scrolling as the escape hatch, not a surprise for low-vision users.
Finally, make the check automatic. A dedicated overflow test file per critical screen — checkout, sign-up, settings — runs in seconds under flutter test and fails the pull request before broken layout reaches testers. Pair it with golden screenshots at the smallest width so reviewers see the layout, not just a green check. Screens covered by this matrix stop generating overflow tickets, release after release.
German Labels Overflowed Checkout for 23K Sessions Before the Hotfix
- Test layouts at 320 logical pixels with your longest locale, not at 800 pixels in English. Overflow is a function of width times string length, so your test matrix must multiply both extremes or it proves nothing.
- Overflow never crashes, so crash reporting will not save you. Assert tester.takeException is null in widget tests and watch business metrics like checkout completion per screen-size bucket after every release.
- Give exactly one child per Row the job of absorbing slack via Expanded. When every child demands its ideal size, the Row has no safe loser — so the framework paints the stripe instead of guessing.
| File | Command / Code | Purpose |
|---|---|---|
| io | class OrderSummary extends StatelessWidget { | Reading the Stripe |
| io | class OrderRowFixed extends StatelessWidget { | Fixing Row Overflow With Expanded and Flexible |
| io | class CheckoutForm extends StatelessWidget { | Fixing Column Overflow |
| io | class AdaptiveSummary extends StatelessWidget { | LayoutBuilder |
| io | class CartPage extends StatelessWidget { | Unbounded Height |
Key takeaways
Common mistakes to avoid
5 patternsWrapping every Row child in Expanded
Putting a ListView inside a Column with no bound
Hiding the stripe with clipping instead of fixing layout
Testing only in English on a large phone
Using fixed widths copied from a design mock
Interview Questions on This Topic
What does A RenderFlex overflowed by 60 pixels on the right actually tell you?
Frequently Asked Questions
20+ years shipping production backend systems. Notes here come from systems that actually shipped.
That's Flutter. Mark it forged?
5 min read · try the examples if you haven't