Flutter SizedBox vs ConstrainedBox vs Container for Sizing

Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter SizedBox vs ConstrainedBox vs Container guide, with fixed dimensions, BoxConstraints min and max, and sizing visuals.
Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter SizedBox vs ConstrainedBox vs Container guide.

Three widgets in Flutter set size, and they are easy to confuse: SizedBox pins an exact dimension, ConstrainedBox sets a range, and Container can do either as a side effect of being a do-everything box. Reaching for the wrong one means heavier widget trees, layouts that don't react to constraints the way you expect, and the occasional silent "my SizedBox did nothing" surprise. This guide draws the line between all three, with paste-ready code.

The Complete Flutter Guide course thumbnail

The Complete Flutter Guide: Build Android, iOS and Web apps

Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.

Enrol now

Every snippet below is paste-ready against current stable Flutter. Sizing only makes sense in the context of Flutter's constraints model, so if "constraints go down, sizes go up" isn't second nature yet, the Layout Widgets guide is the prerequisite this post builds on.

Follow me on Instagram@sagnikteaches

We'll cover what SizedBox does and its shrink/expand shortcuts, how ConstrainedBox and BoxConstraints set a range instead of a point, when Container earns its place, FractionallySizedBox for proportional sizing, and a clear rule for picking one every time.

Connect on LinkedInSagnik Bhattacharya

If you learn faster by watching code take shape, the channel builds these sizing patterns inside real screens where the choice between the three actually changes the result.

Subscribe on YouTube@codingliquids

SizedBox: a single, exact size

SizedBox forces its child to a specific width and/or height. Either dimension can be omitted, in which case that axis is left to the parent's constraints.

SizedBox(
  width: 200,
  height: 48,
  child: ElevatedButton(onPressed: () {}, child: const Text('Save')),
)

Its second job is the one you'll use even more often: empty space. A SizedBox with a height and no child is the standard, allocation-free way to put a gap between widgets in a Column or Row.

Column(
  children: const [
    Text('Title'),
    SizedBox(height: 12),   // a clean 12px gap
    Text('Subtitle'),
  ],
)

Two named constructors round it out. SizedBox.shrink() is a zero-by-zero box — handy as a "render nothing" placeholder in a conditional. SizedBox.expand() sets both dimensions to infinity so the box fills whatever space the parent permits.

const SizedBox.shrink()    // 0 x 0 — an empty placeholder
const SizedBox.expand()    // fills all available space

ConstrainedBox: a range, not a point

Where SizedBox dictates one size, ConstrainedBox sets bounds and lets the child choose within them. You pass it a BoxConstraints describing minimum and maximum width and height.

ConstrainedBox(
  constraints: const BoxConstraints(
    minWidth: 120,
    maxWidth: 320,
    minHeight: 48,
  ),
  child: Text('I grow with my text, but never past 320px wide'),
)

This is the right tool for "at least this big, at most that big" — a button that should never be narrower than 120 logical pixels but is free to grow with a long label, or a card that caps its width on a wide tablet so the line length stays readable. The child sizes itself naturally and the constraints simply clamp the result.

BoxConstraints also has expressive named constructors: BoxConstraints.tightFor(width: 100) pins one axis while leaving the other free, BoxConstraints.loose(size) allows zero up to a maximum, and BoxConstraints.expand() demands the full available space. Most of the time the plain constructor with the min/max fields you need is the clearest.

The Complete Flutter Guide course thumbnail

Understand constraints once, debug layouts forever

The Complete Flutter Guide teaches Flutter's constraint model so sizing stops being trial and error.

Enrol now

Container: sizing plus everything else

Container can set a width and height too, which is why people reach for it out of habit. But Container is a convenience widget that internally composes several others — alignment, padding, decoration, constraints, and sizing — so using it purely to set a dimension wraps your child in machinery it doesn't need.

// Overkill if all you want is a size:
Container(width: 200, height: 48, child: child)

// Lighter, and can be const:
const SizedBox(width: 200, height: 48, child: child)

The moment you add a decoration, the calculus flips: if the box needs a background colour, border, gradient, or rounded corners and a size, Container is exactly right — it would be silly to nest a DecoratedBox inside a SizedBox by hand. The full breakdown of what Container bundles lives in the Container widget guide.

FractionallySizedBox: proportional sizing

When you want a child to take a fraction of the space its parent offers — half the width, a third of the height — FractionallySizedBox expresses that directly with widthFactor and heightFactor between 0 and 1.

FractionallySizedBox(
  widthFactor: 0.6,            // 60% of the available width
  child: ElevatedButton(onPressed: () {}, child: const Text('Continue')),
)

This reacts to local constraints, which is usually what you want — a button that is 60% of its row will adapt on every screen size without you reading MediaQuery. Reach for MediaQuery.of(context).size only when you genuinely need to size against the whole device rather than the immediate parent.

Why a SizedBox sometimes seems to do nothing

A common surprise: you wrap a widget in SizedBox(width: 50, height: 50) and the child ignores it. This happens because some widgets impose their own constraints. For example, an Image with a fixed source size, or a child already given tight constraints by a parent Expanded, can override what the SizedBox asks for. SizedBox sets the constraints it passes down; if a stronger constraint sits above or the child is inflexible, the visible size won't match. When that happens, check the chain of parents — the constraint actually in force is the one nearest the child that is tight.

Choosing the right widget

  • Use SizedBox for an exact width/height, or as the cleanest way to add empty space between widgets.
  • Use ConstrainedBox when you need a minimum or maximum but want the child to size itself within that range.
  • Use Container when you need a size and a decoration, padding, margin, or alignment in one widget.
  • Use FractionallySizedBox for a size expressed as a fraction of the available space.

Common mistakes

  • Using Container just to set a size. Prefer SizedBox — it is lighter and can be const.
  • Expecting SizedBox to win against tight parent constraints. The nearest tight constraint to the child decides; a SizedBox can't override an Expanded above it.
  • Reaching for ConstrainedBox when you mean an exact size. If min equals max you want a SizedBox; constraints are for ranges.
  • Hand-rolling percentage maths with MediaQuery. FractionallySizedBox is usually cleaner and reacts to local constraints, not just device size.
  • Adding empty Containers for spacing. A childless SizedBox communicates intent better and allocates nothing extra.

Frequently asked questions

What is the difference between SizedBox and ConstrainedBox in Flutter?

SizedBox sets one exact size; ConstrainedBox sets a range via BoxConstraints (min/max width and height) and lets the child choose within it.

Should I use SizedBox or Container in Flutter?

Use SizedBox for size or spacing alone — it is lighter and can be const. Use Container when you also need a decoration, padding, margin, or alignment.

What does SizedBox.expand do in Flutter?

It sets width and height to infinity so the box fills the space the parent allows. SizedBox.shrink is its zero-size counterpart.

How do I size a widget as a percentage of the screen in Flutter?

Use FractionallySizedBox with widthFactor/heightFactor for a fraction of the parent's space, or read MediaQuery.of(context).size to size against the whole device.

Further reads

Keep going with the tutorials that pair with this guide:

Sources: Flutter documentation — SizedBox, ConstrainedBox, BoxConstraints, Container, and FractionallySizedBox API references, plus the Understanding constraints article (docs.flutter.dev). Verified against current stable Flutter.