What is the difference between Expanded and Flexible?

Medium PriorityAsked in ~60% of Flutter interviews

2 min read

Widgets & Layout

ExpandedFlexible
FlexFittight (must fill its slot)loose (can shrink)
Child sized bythe Flex layoutthe child itself, up to the slot size
Default flex11
Use forEqual 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

GoalUse
Make a single child consume "the rest" of the row/columnExpanded
Build equal-width columnsExpanded with same flex
Allow long text to wrap but shrink if shortFlexible
Let a child shrink while still capping at its shareFlexible
Insert pure empty spaceSpacer (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

  1. What does flex mean numerically? It's a ratio, not pixels. After fixed-size children are laid out, the remaining space is divided into Σ flex parts. Each Expanded/Flexible gets flex / Σ flex of it.

  2. When would you choose Flexible over Expanded? 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.

  3. What's a Spacer and how does it relate to these two? Spacer is sugar for Expanded(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.

  4. Why does Flutter complain if you put Expanded inside a SingleChildScrollView? Expanded requires 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.