Dart Tutorial

Flutter Lesson 16 of 83 3 min read

Expanded, Flexible and Spacer in Flutter

Learn how Expanded, Flexible and Spacer share space inside a Row or Column in Flutter, how flex works and how they prevent overflow.

On this page

Inside a Row or Column, children are their natural size unless told otherwise. Expanded and Flexible let a child take a share of the space that is left over.

Expanded: take the remaining space #

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: SafeArea(
          child: Column(
            children: [
              Container(height: 60, color: Colors.indigo),
              Expanded(child: Container(color: Colors.amber)),
              Container(height: 60, color: Colors.indigo),
            ],
          ),
        ),
      ),
    ),
  );
}

The top and bottom bars are 60 pixels each. The middle fills whatever remains, on any screen size. This header, content, footer shape is one of the most common layouts.

How space is shared #

  1. Children without Expanded are measured first and take the size they need.
  2. The space left over is divided among the Expanded children.

flex: sharing in proportion #

Row(
  children: [
    Expanded(flex: 2, child: Container(height: 48, color: Colors.red)),
    Expanded(flex: 1, child: Container(height: 48, color: Colors.green)),
    Expanded(flex: 1, child: Container(height: 48, color: Colors.blue)),
  ],
)

The flex values add up to 4, so red gets two quarters of the width and the others one quarter each. The default is 1, so several plain Expanded widgets share equally.

The standard fix for overflow #

A long Text in a Row overflows because the row lets it be as wide as it wants. Expanded gives it a limit, so it wraps or truncates.

Row(
  children: [
    const Icon(Icons.location_on),
    const SizedBox(width: 8),
    Expanded(
      child: Text(
        address,
        maxLines: 1,
        overflow: TextOverflow.ellipsis,
      ),
    ),
    TextButton(onPressed: () {}, child: const Text('Change')),
  ],
)

Flexible: may be smaller #

Expanded forces its child to fill its share. Flexible lets the child be smaller if it wants.

Row(
  children: [
    Flexible(
      child: Container(
        color: Colors.amber,
        padding: const EdgeInsets.all(8),
        child: const Text('Short'),
      ),
    ),
    Container(width: 80, height: 40, color: Colors.indigo),
  ],
)

The amber box is only as wide as the word “Short”. Change Flexible to Expanded and it stretches to fill the row.

ExpandedFlexible
Takes a share of free spaceYesUp to that share
Child must fill the shareYesNo
Same asFlexible(fit: FlexFit.tight)Flexible(fit: FlexFit.loose)

Use Flexible when text should take only the width it needs but must still shrink on a small screen, such as a chat bubble.

Spacer #

Spacer is an empty Expanded. It pushes its neighbours apart.

Row(
  children: const [
    Text('Logo'),
    Spacer(),
    Icon(Icons.search),
    SizedBox(width: 16),
    Icon(Icons.menu),
  ],
)

Rules #

  • Expanded, Flexible and Spacer only work as direct children of a Row, Column or Flex. Anywhere else you get “Incorrect use of ParentDataWidget”.
  • They do not work inside a ListView or SingleChildScrollView along the scroll direction, because the available space there is infinite.
  • Putting an Expanded in a Column that sits inside another Column needs the outer one to give it a bounded height, usually with another Expanded.

Try it yourself #

Build a chat input bar: a text field that takes all the free width, with a send button of fixed size on the right. Then build a progress bar from two Expanded widgets whose flex values come from a percent variable.

Practise in the playground Updated by Santosh Adhikari