Flutter LayoutBuilder: Build Widgets That Adapt to Constraints

Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter LayoutBuilder guide, with BoxConstraints maxWidth breakpoints switching between narrow and wide adaptive layouts.
Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter LayoutBuilder guide.

Most responsive code reaches for MediaQuery and the screen size — but a widget rarely owns the whole screen. LayoutBuilder hands you the exact BoxConstraints your widget has been given, so a card can lay itself out one way inside a narrow phone column and another way inside a wide tablet pane, without ever knowing how big the screen is. This guide covers how LayoutBuilder exposes constraints, building breakpoints from maxWidth, how it differs from MediaQuery, the infinite-constraint trap, and when to use each, 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. LayoutBuilder is the constraint-aware sibling of the layout widgets in the layout widgets guide — and understanding constraints is what makes it click.

Follow me on Instagram@sagnikteaches

We'll read the constraints, branch on maxWidth for a breakpoint, build an adaptive grid, compare against MediaQuery, and avoid the infinite-constraint crash.

Connect on LinkedInSagnik Bhattacharya

If you'd rather watch adaptive layouts built across phone, tablet, and desktop, the channel covers responsive Flutter end to end.

Subscribe on YouTube@codingliquids

Reading the constraints

LayoutBuilder takes a builder callback that receives the BuildContext and the BoxConstraints the parent passed down. The most useful field is maxWidth — the widest the widget is allowed to be in its current slot.

LayoutBuilder(
  builder: (context, constraints) {
    return Text('I can be up to '
        '${constraints.maxWidth.toStringAsFixed(0)} px wide');
  },
)

The key idea: this is the size of this widget's box, not the screen. Place the same LayoutBuilder inside a half-width pane and maxWidth is half the screen — which is exactly what makes component-level responsiveness possible.

A maxWidth breakpoint

The classic pattern is to switch layout once the available width crosses a threshold. Below the breakpoint you show a single column; above it, a two-column row.

LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth >= 600) {
      return Row(
        children: const [
          Expanded(child: DetailsPanel()),
          Expanded(child: SidebarPanel()),
        ],
      );
    }
    return Column(
      children: const [DetailsPanel(), SidebarPanel()],
    );
  },
)

Because the decision uses the widget's own constraints, this card behaves correctly whether it fills a phone screen or sits in a narrow column of a desktop dashboard — no manual screen-size maths required.

The Complete Flutter Guide course thumbnail

Adaptive UIs, built properly

The Complete Flutter Guide covers constraints, breakpoints, and responsive design from the ground up.

Enrol now

An adaptive grid

Combine LayoutBuilder with a GridView to pick a column count from the available width — more columns as the panel grows wider.

LayoutBuilder(
  builder: (context, constraints) {
    final columns = (constraints.maxWidth / 200).floor().clamp(1, 4);
    return GridView.count(
      crossAxisCount: columns,
      children: products.map(ProductCard.new).toList(),
    );
  },
)

Dividing the available width by a target tile size gives a natural, fluid column count. For grids specifically, SliverGridDelegateWithMaxCrossAxisExtent does the same job built in — but LayoutBuilder is the general tool when the branching is more than column counting.

LayoutBuilder vs MediaQuery

Both answer "how much space do I have?", but at different scopes. MediaQuery.of(context).size reports the whole screen or window — right for page-level decisions like the overall navigation shell. LayoutBuilder reports the constraints of the specific box the widget occupies — right when a single component should adapt to its slot.

A good rule: use MediaQuery to decide the page skeleton (see the responsive Flutter UI guide), and LayoutBuilder to make individual widgets inside that skeleton adapt to the room they're given.

The infinite-constraint trap

LayoutBuilder reports whatever the parent passes. If the parent imposes no horizontal bound — inside a horizontal ListView, or a Row without an Expanded — then maxWidth is double.infinity, and comparing it to a breakpoint silently picks the "wide" branch forever or throws.

LayoutBuilder(
  builder: (context, constraints) {
    if (!constraints.hasBoundedWidth) {
      return const NarrowLayout(); // safe fallback
    }
    return constraints.maxWidth >= 600
        ? const WideLayout()
        : const NarrowLayout();
  },
)

Guard with constraints.hasBoundedWidth, or give the LayoutBuilder a bounded parent, before you branch on the number.

Common mistakes

  • Using MediaQuery for component-level adaptivity. A widget rarely owns the full screen; use LayoutBuilder for its own slot.
  • Branching on an infinite maxWidth. Guard with hasBoundedWidth or bound the parent.
  • Heavy work inside the builder. It runs during layout and on every constraint change; keep it light.
  • Forgetting it rebuilds on resize and rotation. That's the point, but don't assume it runs only once.
  • Reaching for LayoutBuilder when a Flexible would do. Often plain constraints widgets solve it without a builder.

Frequently asked questions

What is LayoutBuilder used for in Flutter?

It exposes the BoxConstraints from the parent so a widget can adapt to the space it's actually given, rather than to the whole screen.

What is the difference between LayoutBuilder and MediaQuery in Flutter?

MediaQuery reports the screen size for page-level breakpoints; LayoutBuilder reports the widget's own box for component-level adaptivity.

Why does LayoutBuilder say maxWidth is infinity in Flutter?

Its parent imposed no width bound — a horizontal ListView or unconstrained Row. Guard with hasBoundedWidth or give it a bounded parent.

Does LayoutBuilder hurt performance in Flutter?

It rebuilds the child on constraint changes, which is usually cheap. Keep the builder body light since it can run during layout.

Further reads

Keep going with the tutorials that pair with this guide:

Sources: Flutter documentation — LayoutBuilder, BoxConstraints, MediaQuery, and the "Understanding constraints" guide (docs.flutter.dev). Verified against current stable Flutter.