Flutter Responsive Layouts

Phones, tablets, foldables, browsers, and desktop windows all run the same Flutter code. A layout that looks perfect on a small phone may leave huge empty margins on a tablet. Responsive design means the screen adapts to the space available, like water that takes the shape of any glass.

Screen Size Ranges

Developers group screens into ranges called breakpoints. These common values work well as a starting point:

RangeWidthTypical Device
CompactUnder 600Phone
Medium600 to 900Tablet, foldable
ExpandedOver 900Laptop, desktop

  0        600           900
  ├─────────┼─────────────┼──────────────→ width
  │ phone   │  tablet     │   desktop
  │ 1 column│  2 columns  │   3 columns

MediaQuery

MediaQuery reports facts about the whole screen: width, height, orientation, and text scale.


final size = MediaQuery.of(context).size;
final width = size.width;
final isPortrait = MediaQuery.of(context).orientation == Orientation.portrait;

Use it when a decision depends on the full window.

LayoutBuilder

LayoutBuilder reports the space the parent gives to a widget. It suits reusable components, because the widget reacts to its own box and ignores the rest of the screen.


LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth > 600) {
      return WideLayout();
    }
    return NarrowLayout();
  },
)

A Two-Panel Example

A mail app shows a list of messages on a phone and a list beside a reading pane on a tablet.


  Phone                     Tablet
  ┌───────────┐             ┌────────┬────────────┐
  │ Inbox list│             │ Inbox  │ Message    │
  │           │             │ list   │ detail     │
  │           │             │        │            │
  └───────────┘             └────────┴────────────┘

Widget build(BuildContext context) {
  return LayoutBuilder(
    builder: (context, constraints) {
      if (constraints.maxWidth > 700) {
        return Row(
          children: [
            Expanded(flex: 1, child: InboxList()),
            Expanded(flex: 2, child: MessageDetail()),
          ],
        );
      }
      return InboxList();
    },
  );
}

Flexible Building Blocks

Expanded and Flexible

Inside a Row or Column, Expanded fills leftover space. The flex number divides space by ratio: a child with flex: 2 receives twice the room of a child with flex: 1. Flexible lets a child shrink below the available space.

FractionallySizedBox

This widget sizes a child as a fraction of its parent.


FractionallySizedBox(
  widthFactor: 0.8,
  child: ElevatedButton(onPressed: () {}, child: Text('Continue')),
)

AspectRatio

AspectRatio keeps a fixed shape, such as 16 by 9 for video, at every width.


AspectRatio(
  aspectRatio: 16 / 9,
  child: Image.asset('assets/banner.png', fit: BoxFit.cover),
)

Wrap

Wrap works like a Row that moves extra items to a new line. Tag chips and filter buttons fit this pattern.

SafeArea

Modern phones have notches, rounded corners, and gesture bars. SafeArea pushes content away from these zones so nothing hides behind them.


Scaffold(
  body: SafeArea(
    child: Text('Visible content'),
  ),
)

Text Scaling

Users with low vision raise the system font size. Avoid fixed-height boxes around text, because larger text overflows the box and triggers a yellow-black striped error. Let containers grow with their content.

Orientation Changes

OrientationBuilder rebuilds a layout when the user rotates the device. Switch a two-column grid to four columns in landscape.


OrientationBuilder(
  builder: (context, orientation) {
    return GridView.count(
      crossAxisCount: orientation == Orientation.portrait ? 2 : 4,
      children: items,
    );
  },
)

Testing Your Layout

  • Resize the browser window when you run flutter run -d chrome.
  • Try a small phone emulator and a tablet emulator.
  • Rotate the device in both directions.
  • Raise the system font size in device settings.

Choose LayoutBuilder for component-level decisions and MediaQuery for screen-level decisions. Combine both with flexible widgets, and the app looks right everywhere.

Adaptive Versus Responsive

Responsive design stretches and rearranges one layout across sizes. Adaptive design goes further and changes behavior for each platform. A phone app shows a bottom navigation bar, while a desktop app shows a side rail with labels. Flutter supports both styles through NavigationBar and NavigationRail, and a LayoutBuilder decides which one appears.

Input Differences

Phones use fingers, and desktops use a mouse and keyboard. Buttons on touch screens need at least 48 pixels of height for comfortable tapping. Desktop users expect hover effects, scroll bars, and keyboard shortcuts. Test with real input methods when you target several platforms.