How do you create responsive layouts in Flutter?
3 min read
Widgets & UI
Theory — which API for which question
| Question you're answering | Use |
|---|---|
| "How wide is the parent giving me?" (local) | LayoutBuilder |
| "How wide is the device / window?" (global) | MediaQuery.sizeOf(context) |
| "Share space proportionally inside a row/column" | Expanded, Flexible |
| "Take a fraction of the parent" | FractionallySizedBox |
| "Maintain a ratio" | AspectRatio |
| "Wrap items onto new lines as space runs out" | Wrap |
| "Grid where items have a target max width" | GridView + SliverGridDelegateWithMaxCrossAxisExtent |
Common breakpoints (Material): < 600 mobile, 600–840 tablet, > 840 desktop.
Code in action
// 1️⃣ Page-level: branch on device width
@override
Widget build(BuildContext context) {
final w = MediaQuery.sizeOf(context).width;
if (w >= 900) return const DesktopLayout();
if (w >= 600) return const TabletLayout();
return const MobileLayout();
}
// 2️⃣ Local-level: branch on parent constraints (better!)
LayoutBuilder(
builder: (ctx, c) => c.maxWidth < 600
? const _List()
: const _Grid(),
);
// 3️⃣ Flexible columns
Row(
children: const [
Expanded(flex: 2, child: ContentPane()),
Expanded(flex: 1, child: Sidebar()),
],
);
// 4️⃣ Fluid grid — items decide how many columns fit
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 220,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 1,
),
itemCount: items.length,
itemBuilder: (ctx, i) => ItemCard(items[i]),
);
// 5️⃣ Wrap — chips, tags, anything that should flow to next line
Wrap(spacing: 8, runSpacing: 8, children: chips);
// 6️⃣ Aspect-ratio preserving — videos, cover art
AspectRatio(aspectRatio: 16 / 9, child: VideoPlayer(controller));
LayoutBuilder vs MediaQuery — which one?
Use LayoutBuilder when… | Use MediaQuery when… |
|---|---|
| The widget could be anywhere in the tree (sidebar, dialog, sheet) | You're branching at the page level on device size |
| You care about available space, not screen size | You need orientation, padding, brightness, text scale |
| You want to be embeddable in a smaller container | You want a single breakpoint for the whole app |
A common bug: using MediaQuery.sizeOf(context) inside a card that's actually 300px wide — you'd render the desktop layout in a tiny corner.
Common mistakes to avoid
// ❌ Hard-coded pixel widths
SizedBox(width: 320, child: ...); // breaks on small screens
// ✅ Use Expanded/FractionallySizedBox or branch on constraints
// ❌ MediaQuery.of(context) when you only need size
final mq = MediaQuery.of(context); // subscribes to ALL metrics
// ✅ MediaQuery.sizeOf(context) — rebuilds only when size changes
// ❌ Branching on device size inside a small embedded widget
LayoutBuilder(builder: (ctx, c) { // good
return MediaQuery.of(ctx).size.width > 600 ... // wrong inside a side panel
});
// ❌ Forgetting text scale
// Users can set huge font sizes — test with MediaQuery.textScalerOf
// ❌ Trusting orientation alone
// A foldable in landscape vs phone in landscape are different widths — branch on size, not orientation
Interview follow-ups
-
LayoutBuildervsMediaQuery— when do you use each?LayoutBuilderanswers "how much space did my parent give me?" — perfect for self-contained, embeddable widgets.MediaQueryanswers "what's the device like?" — best for app-wide breakpoints and reading insets/padding/orientation. -
Why does
MediaQuery.sizeOf(context)exist alongsideMediaQuery.of(context)? Theofversion subscribes to everyMediaQueryDatafield — so a keyboard appearing causes a rebuild even if you only care about screen size..sizeOf,.paddingOf,.textScalerOfetc. let you subscribe to one slice and avoid spurious rebuilds. -
How do you handle text scaling for accessibility? Don't fight it — use
Text(it scales by default), avoid hard-coded heights for text containers, test at large text scales. For controlled layouts, readMediaQuery.textScalerOf(context)and adjust. -
What's the cleanest way to build a 1-column / 2-column / 3-column responsive grid? Use
GridView.builderwithSliverGridDelegateWithMaxCrossAxisExtent— set the max item width, and Flutter computes the column count for you. Adapts smoothly across phone, tablet, and desktop.
How helpful was this content?
Please sign in to rate this article.