Flutter Lesson 58 of 83 4 min read
MediaQuery and LayoutBuilder in Flutter
Learn to read the screen size with MediaQuery and the available space with LayoutBuilder, and when to use each for responsive Flutter UI.
On this page
Two tools answer the question “how much room do I have?” They answer it differently, and the difference matters.
MediaQuery | LayoutBuilder | |
|---|---|---|
| Tells you | About the whole screen and device settings | The space this widget has been given |
| Use for | App-level decisions, keyboard, notches, text scale | Component-level layout decisions |
MediaQuery #
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: InfoPage()));
class InfoPage extends StatelessWidget {
const InfoPage({super.key});
@override
Widget build(BuildContext context) {
final size = MediaQuery.sizeOf(context);
final orientation = MediaQuery.orientationOf(context);
final padding = MediaQuery.paddingOf(context);
final brightness = MediaQuery.platformBrightnessOf(context);
return Scaffold(
body: Center(
child: Text(
'Size: ${size.width.round()} x ${size.height.round()}\n'
'Orientation: ${orientation.name}\n'
'Top inset: ${padding.top.round()}\n'
'System theme: ${brightness.name}',
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20),
),
),
);
}
}
Resize the window, or rotate the device, and the widget rebuilds with new values.
| Accessor | Gives |
|---|---|
MediaQuery.sizeOf(context) | Screen (or window) width and height in logical pixels |
MediaQuery.orientationOf(context) | Portrait or landscape |
MediaQuery.paddingOf(context) | Space taken by notches, status bar and gesture bar |
MediaQuery.viewInsetsOf(context) | Space covered by the keyboard |
MediaQuery.textScalerOf(context) | The user’s text size setting |
MediaQuery.platformBrightnessOf(context) | The device’s light or dark setting |
MediaQuery.devicePixelRatioOf(context) | Physical pixels per logical pixel |
MediaQuery.disableAnimationsOf(context) | Whether the user asked for reduced motion |
Use these specific accessors, not MediaQuery.of(context).size. With sizeOf, the widget rebuilds only when the size changes. With of(context), it rebuilds when anything changes, including each frame of the keyboard sliding up.
Logical pixels #
All sizes in Flutter are logical pixels, not physical ones. A 100-pixel box is about the same physical size on every screen, whatever its resolution. A typical phone is 360 to 430 logical pixels wide, a tablet 600 to 1000, a desktop window more.
LayoutBuilder #
LayoutBuilder gives the constraints from the parent: how big this particular widget may be.
import 'package:flutter/material.dart';
class ProductTile extends StatelessWidget {
const ProductTile({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
const image = SizedBox(width: 120, height: 120, child: ColoredBox(color: Colors.teal));
const details = Padding(
padding: EdgeInsets.all(12),
child: Text('Trail backpack\nRs. 3,499'),
);
// Wide enough: image beside the text. Otherwise: image above.
if (constraints.maxWidth > 300) {
return const Row(children: [image, Expanded(child: details)]);
}
return const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [image, details],
);
},
);
}
}
void main() {
runApp(
const MaterialApp(
home: Scaffold(
body: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(flex: 3, child: Card(child: ProductTile())),
Expanded(flex: 1, child: Card(child: ProductTile())),
],
),
),
),
);
}
Both tiles are on the same screen, but they get different widths and choose different layouts. MediaQuery could not do this, because both would see the same screen size.
Which one to use #
Prefer LayoutBuilder for components. A card, a form or a list should adapt to the space it has. Then it works in a full-width phone screen, in half of a tablet, in a dialog and in a side panel, with no changes.
Use MediaQuery for:
- Top-level decisions: bottom navigation or a side rail?
- The keyboard:
viewInsetsOf(context).bottom. - Safe areas, text scale and reduced motion.
Avoid sizing by percentage of the screen #
A common beginner habit:
// Fragile
Container(width: MediaQuery.sizeOf(context).width * 0.8, height: MediaQuery.sizeOf(context).height * 0.3)
This produces a tiny box on a small phone and a huge one on a tablet, and text inside may not fit either. Let content size itself, and use the layout tools:
| Goal | Use |
|---|---|
| Fill the remaining space | Expanded |
| Share space in proportion | Expanded(flex: ...) |
| Never exceed a width | ConstrainedBox(constraints: BoxConstraints(maxWidth: 600)) |
| A fraction of the parent | FractionallySizedBox(widthFactor: 0.8) |
| Keep a shape | AspectRatio(aspectRatio: 16 / 9) |
| Flow onto more lines | Wrap |
| Shrink to fit | FittedBox, Flexible |
Do not scale everything with the screen #
Some packages scale every font and size by the screen width. The result is that a tablet shows the same content as a phone, only bigger, which wastes the extra space. A larger screen should show more, not larger. Use breakpoints to change the layout, as the next lesson shows.
Text scale #
Users can enlarge text in their settings, sometimes to double size. Flutter applies this to every Text automatically. Your part is to not break it: avoid fixed heights around text, let text wrap, and test with large text enabled.
Try it yourself #
Build a ProfileCard that uses LayoutBuilder: with less than 400 pixels of width it stacks an avatar above the name and bio, and with more it places the avatar on the left. Show it twice on one screen at different widths.