← Back to Articles

Flutter Layouts: Understanding Row, Column, and Flex

Flutter Layouts: Understanding Row, Column, and Flex

Flutter Layouts: Understanding Row, Column, and Flex

When you're building Flutter apps, one of the first things you'll encounter is arranging widgets on the screen. Whether you're placing buttons side by side, stacking text vertically, or creating complex responsive layouts, Flutter's layout widgets are your best friends. Today, we're diving into three fundamental layout widgets: Row, Column, and Flex. These widgets form the backbone of most Flutter UIs, and understanding them will make your development journey much smoother.

What Are Layout Widgets?

Layout widgets in Flutter are containers that arrange their children in a specific pattern. Unlike widgets that display content (like Text or Image), layout widgets focus on positioning and sizing other widgets. Think of them as invisible boxes that organize your UI elements.

The three widgets we're exploring today—Row, Column, and Flex—all arrange children in a single direction. Row places widgets horizontally, Column arranges them vertically, and Flex is the flexible parent that can do either based on your configuration.

Layout Widget Comparison Row Horizontal Column Vertical Flex Configurable

Row: Arranging Widgets Horizontally

Row is perfect when you want to place widgets side by side. Imagine a toolbar with buttons, a navigation bar with icons, or a card with an image and text next to each other. That's where Row shines.

Let's start with a simple example:


Row(
  children: [
    Text('Hello'),
    Text('World'),
    Text('Flutter'),
  ],
)

This creates three Text widgets placed horizontally. By default, Row will try to fit all children in the available space, but what happens when there's not enough room? That's where alignment and sizing come into play.

MainAxisAlignment: Controlling Horizontal Alignment

The mainAxis in a Row is horizontal (left to right). You can control how children are distributed along this axis using MainAxisAlignment:


Row(
  mainAxisAlignment: MainAxisAlignment.start, // Aligns to the left
  children: [
    ElevatedButton(onPressed: () {}, child: Text('Button 1')),
    ElevatedButton(onPressed: () {}, child: Text('Button 2')),
    ElevatedButton(onPressed: () {}, child: Text('Button 3')),
  ],
)

Common MainAxisAlignment values include:

  • start: Aligns children to the beginning (left side)
  • end: Aligns children to the end (right side)
  • center: Centers children horizontally
  • spaceBetween: Places equal space between children
  • spaceAround: Places equal space around each child
  • spaceEvenly: Distributes space evenly including edges

CrossAxisAlignment: Controlling Vertical Alignment

The crossAxis in a Row is vertical (top to bottom). CrossAxisAlignment controls how children align vertically within the Row:


Row(
  crossAxisAlignment: CrossAxisAlignment.center, // Centers vertically
  children: [
    Container(
      width: 50,
      height: 100,
      color: Colors.blue,
    ),
    Container(
      width: 50,
      height: 50,
      color: Colors.green,
    ),
    Container(
      width: 50,
      height: 75,
      color: Colors.red,
    ),
  ],
)

Common CrossAxisAlignment values are start, center, end, stretch, and baseline.

Row Alignment Options mainAxisAlignment start mainAxisAlignment center mainAxisAlignment spaceBetween crossAxisAlignment start crossAxisAlignment center crossAxisAlignment stretch

Column: Arranging Widgets Vertically

Column works exactly like Row, but arranges children vertically instead of horizontally. It's perfect for forms, lists of items, or any vertical layout. The mainAxis in a Column is vertical (top to bottom), and the crossAxis is horizontal (left to right).


Column(
  children: [
    Text('First Item'),
    Text('Second Item'),
    Text('Third Item'),
  ],
)

Just like Row, Column supports MainAxisAlignment and CrossAxisAlignment, but their meanings are flipped:


Column(
  mainAxisAlignment: MainAxisAlignment.center, // Centers vertically
  crossAxisAlignment: CrossAxisAlignment.start, // Aligns horizontally to left
  children: [
    ElevatedButton(onPressed: () {}, child: Text('Top')),
    ElevatedButton(onPressed: () {}, child: Text('Middle')),
    ElevatedButton(onPressed: () {}, child: Text('Bottom')),
  ],
)

Handling Overflow

One common issue with Column is overflow. When children take up more space than available, Flutter will throw an error. You can solve this by wrapping your Column in a SingleChildScrollView or using Expanded/Flexible widgets:


SingleChildScrollView(
  child: Column(
    children: [
      // Many children here
    ],
  ),
)

Flex: The Flexible Parent

Flex is the base class for both Row and Column. You typically won't use Flex directly, but understanding it helps you grasp how Row and Column work. Flex takes a direction parameter that determines whether it arranges children horizontally or vertically:


Flex(
  direction: Axis.horizontal, // Works like Row
  children: [
    Text('Item 1'),
    Text('Item 2'),
  ],
)

Flex(
  direction: Axis.vertical, // Works like Column
  children: [
    Text('Item 1'),
    Text('Item 2'),
  ],
)

In practice, you'll use Row or Column instead of Flex directly, as they're more convenient and readable.

Flexible and Expanded: Controlling Child Sizes

One of the most powerful features of Row and Column is their ability to distribute space among children using Flexible and Expanded widgets. These widgets allow children to take up available space proportionally.

Expanded: Taking All Available Space

Expanded makes a child fill the remaining space along the main axis. If multiple Expanded widgets exist, they share the space equally:


Row(
  children: [
    Expanded(
      child: Container(
        color: Colors.blue,
        child: Text('Takes 1/3 of space'),
      ),
    ),
    Expanded(
      child: Container(
        color: Colors.green,
        child: Text('Takes 1/3 of space'),
      ),
    ),
    Expanded(
      child: Container(
        color: Colors.red,
        child: Text('Takes 1/3 of space'),
      ),
    ),
  ],
)

Flexible: Taking Space When Needed

Flexible is similar to Expanded but more lenient. It allows a child to take up space if available, but doesn't force it to expand:


Row(
  children: [
    Container(
      width: 100,
      color: Colors.blue,
      child: Text('Fixed width'),
    ),
    Flexible(
      child: Container(
        color: Colors.green,
        child: Text('Takes remaining space'),
      ),
    ),
  ],
)

You can also control the flex ratio using the flex parameter:


Row(
  children: [
    Expanded(
      flex: 2, // Takes 2 parts
      child: Container(color: Colors.blue),
    ),
    Expanded(
      flex: 1, // Takes 1 part
      child: Container(color: Colors.green),
    ),
  ],
)
Expanded and Flexible Widgets Three Expanded widgets share space equally 1/3 1/3 1/3 Flexible with flex ratios (2:1) flex: 2 flex: 1

Real-World Example: Building a Card Layout

Let's put everything together in a practical example. We'll create a card that displays a product with an image, title, description, and action buttons:


Card(
  child: Padding(
    padding: EdgeInsets.all(16.0),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        // Image row
        Row(
          children: [
            Container(
              width: 100,
              height: 100,
              color: Colors.grey[300],
              child: Icon(Icons.image),
            ),
            SizedBox(width: 16),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Product Name',
                    style: TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  SizedBox(height: 8),
                  Text(
                    'This is a detailed description of the product that might span multiple lines.',
                  ),
                ],
              ),
            ),
          ],
        ),
        SizedBox(height: 16),
        // Action buttons row
        Row(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            TextButton(
              onPressed: () {},
              child: Text('Cancel'),
            ),
            SizedBox(width: 8),
            ElevatedButton(
              onPressed: () {},
              child: Text('Buy Now'),
            ),
          ],
        ),
      ],
    ),
  ),
)

In this example, we use:

  • A Column to stack the image section and buttons vertically
  • A Row to place the image and text side by side
  • Expanded to make the text section take remaining horizontal space
  • Another Column inside the Expanded to stack the title and description
  • Another Row for the action buttons with end alignment

Common Pitfalls and Tips

As you work with Row and Column, you'll encounter a few common issues:

1. Overflow Errors

When children exceed available space, Flutter throws an overflow error. Solution: Use Expanded, Flexible, or wrap in SingleChildScrollView.

2. Unbounded Constraints

Row and Column need bounded constraints. If you place a Row inside another Row without constraints, you'll get errors. Solution: Wrap in Expanded or provide explicit width constraints.

3. Alignment Confusion

Remember that mainAxis and crossAxis swap meanings between Row and Column. In Row, mainAxis is horizontal; in Column, it's vertical.

4. Spacing Between Children

Use SizedBox for fixed spacing, or MainAxisAlignment.spaceBetween for dynamic spacing. Avoid using Padding on individual children when you want consistent spacing.

Conclusion

Row, Column, and Flex are fundamental building blocks in Flutter development. They provide the structure for almost every UI you'll create. By understanding how they work, how to align their children, and how to use Expanded and Flexible to control sizing, you'll be able to create complex layouts with confidence.

Start with simple examples, experiment with different alignment options, and gradually build more complex layouts. Before you know it, you'll be creating beautiful, responsive UIs that look great on any device. Happy coding!