What is the difference between Expanded and Flexible?
2 min read
Widgets & Layout
Expanded | Flexible | |
|---|---|---|
| FlexFit | tight (must fill its slot) | loose (can shrink) |
| Child sized by | the Flex layout | the child itself, up to the slot size |
Default flex | 1 | 1 |
| Use for | Equal columns, "fill the rest" | Text that wraps, content that may be smaller |
Mental model: a Flex (Row/Column) lays out fixed-size children first, then divides what's left among Expanded/Flexible children by flex. The difference is whether the child must occupy its share.
Code in action
// Expanded → green fills all leftover space
Row(
children: [
Container(width: 50, color: Colors.red),
Expanded(child: Container(color: Colors.green)),
Container(width: 50, color: Colors.blue),
],
)
// [red:50][green:fills rest][blue:50]
// Flex factors → 1 : 2 : 1
Row(
children: [
Expanded(flex: 1, child: ColoredBox(color: Colors.red)),
Expanded(flex: 2, child: ColoredBox(color: Colors.green)),
Expanded(flex: 1, child: ColoredBox(color: Colors.blue)),
],
)
// red 25% : green 50% : blue 25%
// Flexible → text takes only what it needs, no enforced fill
Row(
children: [
const Flexible(child: Text('Short')),
const Flexible(child: Text('A much longer line that can wrap')),
],
)
When to use which
| Goal | Use |
|---|---|
| Make a single child consume "the rest" of the row/column | Expanded |
| Build equal-width columns | Expanded with same flex |
| Allow long text to wrap but shrink if short | Flexible |
| Let a child shrink while still capping at its share | Flexible |
| Insert pure empty space | Spacer (which is Expanded(child: SizedBox.shrink()) under the hood) |
Common mistakes to avoid
// ❌ Wrapping Text in Expanded when you just want overflow handling
Row(children: [Expanded(child: Text('long...'))])
// ✅ Use Flexible (or just rely on softWrap) — Expanded forces fill even when shorter
// ❌ Using Expanded outside a Row/Column/Flex
Container(child: Expanded(child: Text('hi'))); // 💥 Expanded must be a direct Flex child
// ❌ Mixing Expanded and unbounded constraints
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(children: [Expanded(child: ...)]), // 💥 unbounded width → can't expand
);
// ❌ Forgetting flex factors are RELATIVE, not absolute pixels
Expanded(flex: 100, child: ...) // 100 isn't "100 px" — it's just 100 parts of the total
// ❌ Using Container instead of Spacer for spacing inside a Row
Row(children: [A, Container(width: 16), B]);
// ✅ const SizedBox(width: 16) — lighter, const-friendly
Interview follow-ups
-
What does
flexmean numerically? It's a ratio, not pixels. After fixed-size children are laid out, the remaining space is divided intoΣ flexparts. EachExpanded/Flexiblegetsflex / Σ flexof it. -
When would you choose
FlexibleoverExpanded? When the child should be allowed to be smaller than its share — typically text that might be short, or a button that has an intrinsic size you don't want stretched. -
What's a
Spacerand how does it relate to these two?Spaceris sugar forExpanded(child: SizedBox.shrink()). It eats free space with a flex factor and renders nothing. Handy for pushing one item to the end of a row. -
Why does Flutter complain if you put
Expandedinside aSingleChildScrollView?Expandedrequires a bounded main-axis size to know what "fill the rest" means. A scroll view provides unbounded constraints in the scroll direction — so there's no "rest" to fill, and Flutter throws.
How helpful was this content?
Please sign in to rate this article.