Flutter Row Widget

Flutter: Row Widget – Building Horizontal Layouts

In Flutter, the Row widget helps you place widgets side by side in a horizontal line. It’s perfect for buttons, icons, text, or anything you want to place in a row. But it’s more than just putting items next to each other—you can control how they align, how much space they take, and how they respond when space runs out.

In this guide, we’ll explore the Row widget in detail with simple examples, and show how to handle common layout problems like overflow.

What Is the Row Widget?

A Row arranges its children horizontally, from left to right (or right to left depending on the text direction). Each child is a widget, and you can place as many as you want—as long as they fit!

Here’s a simple example that puts an icon and text next to each other:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          children: [Icon(Icons.star, color: Colors.amber), Text('Favorite')],
        ),
      ),
    );
  }
}

This creates a horizontal row showing a star icon followed by the word “Favorite”.

Flutter Row Widget

Main Axis vs Cross Axis

Understanding how alignment works in Row means knowing the two axes:

  • Main Axis (Horizontal) – the direction the children are placed (left to right).
  • Cross Axis (Vertical) – the opposite direction (top to bottom).

We use different properties to control alignment along each axis.

mainAxisAlignment – Aligning Horizontally

This controls how the children are aligned horizontally.

Let’s say you want to spread icons evenly across the row:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            Icon(Icons.home),
            Icon(Icons.search),
            Icon(Icons.settings),
          ],
        ),
      ),
    );
  }
}

Flutter Row Widget

Here’s what each option does:

  • start: Aligns children to the left.
  • end: Aligns to the right.
  • center: Centers them.
  • spaceBetween: Equal space between, no space at ends.
  • spaceAround: Equal space before, between, and after.
  • spaceEvenly: Equal space everywhere.

crossAxisAlignment – Aligning Vertically

This controls how children are aligned along the vertical axis.

In the example below, one widget is taller than the other. We use start to align them both at the top.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [Icon(Icons.download, size: 40), Text('Download')],
        ),
      ),
    );
  }
}

Flutter Row Widget

Options include:

  • start: Aligns at the top.
  • center: Centers vertically.
  • end: Aligns at the bottom.
  • stretch: Fills the vertical space of the row.
  • baseline: Aligns text with a shared baseline (requires Text widgets with same style).

mainAxisSize – Control Row Width

By default, a row stretches to fill all the horizontal space. But if you only want it to be as wide as needed, use mainAxisSize.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          mainAxisSize: MainAxisSize.min,
          children: [Icon(Icons.thumb_up), Text('Like')],
        ),
      ),
    );
  }
}

  • MainAxisSize.min: The row only takes as much space as needed.
  • MainAxisSize.max: The row stretches across the available width (default).
Flutter Row Widget

This sets how much horizontal space the row should take:

textDirection – Changing Child Order

Sometimes you want to flip the order of children. You can use textDirection for that:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          textDirection: TextDirection.rtl,
          children: [Icon(Icons.arrow_back), Text('Back')],
        ),
      ),
    );
  }
}

Now the text comes first, followed by the icon, because the row is reversed right-to-left.

Flutter Row Widget

verticalDirection – Change Vertical Start Point

This controls whether “top” is really the top, or if the row should align from the bottom up.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          verticalDirection: VerticalDirection.up,
          children: [Icon(Icons.upload, size: 50), Text('Upload')],
        ),
      ),
    );
  }
}

With VerticalDirection.up, the top becomes the bottom, so start aligns at the bottom.

Flutter Row Widget

Handling Overflow in a Row

Rows can overflow when the content is too wide for the screen. Here’s a simple overflow problem:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          children: [
            Text('This is a very long line of text that might not fit'),
            Icon(Icons.more_horiz),
          ],
        ),
      ),
    );
  }
}

If this content doesn’t fit the screen width, you’ll get a RenderFlex overflow error. Let’s fix that.

Flutter Row Widget

Solution 1: Make the Row Scrollable

Wrap the Row with SingleChildScrollView and set scroll direction to horizontal:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: Row(
            children: [
              Text('This is a very long line of text that might not fit'),
              Icon(Icons.more_horiz),
            ],
          ),
        ),
      ),
    );
  }
}

Now the row becomes scrollable left-to-right when there isn’t enough space.

Flutter Row Widget

Solution 2: Use Expanded or Flexible

If you want the row’s children to share or stretch into available space, wrap them with Expanded or Flexible.

Expanded – Take All Available Space

Expanded makes a widget take up all the remaining space in the row. Here’s a simple example:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          children: [
            Expanded(child: Container(height: 50, color: Colors.red)),
            Expanded(child: Container(height: 50, color: Colors.green)),
          ],
        ),
      ),
    );
  }
}

Each child takes equal space inside the row. Expanded uses all available width.

Flutter Row Widget

You can also change how much space each takes using the flex property:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          children: [
            Expanded(flex: 2, child: Container(height: 50, color: Colors.red)),
            Expanded(
              flex: 1,
              child: Container(height: 50, color: Colors.green),
            ),
          ],
        ),
      ),
    );
  }
}

Now the red container takes 2 parts, green takes 1 part, so the red one is twice as wide.

Flutter Row Widget

Flexible – Similar to Expanded, But Less Strict

Use Flexible when you want the child to take up space if it can, but allow it to be smaller if needed.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          children: [
            Flexible(
              flex: 2,
              child: Container(height: 50, color: Colors.orange),
            ),
            Flexible(flex: 1, child: Container(height: 50, color: Colors.blue)),
          ],
        ),
      ),
    );
  }
}

This behaves like Expanded, but allows children to size themselves within limits.

Flutter Row Widget

Spacing Between Widgets

To add space manually between widgets, use SizedBox:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          children: [Icon(Icons.phone), SizedBox(width: 10), Text('Call')],
        ),
      ),
    );
  }
}

This adds 10 pixels of space between the icon and text.

Flutter Row Widget

If you’re using a newer Flutter version, you can use the spacing attribute directly:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget',
      home: Scaffold(
        appBar: AppBar(title: Text('Row Widget Example')),
        body: Row(
          spacing: 12,
          children: [
            Icon(Icons.share),
            Icon(Icons.comment),
            Icon(Icons.thumb_up),
          ],
        ),
      ),
    );
  }
}

This adds uniform spacing between all children, making your code cleaner.

Flutter Row Widget

Real-World Example: Simple Bottom Bar

Here’s how you might use a Row for a bottom navigation bar:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Custom Bottom Nav',
      debugShowCheckedModeBanner: false,
      home: const HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home Page')),
      body: const Center(child: Text('Home Page')),
      bottomNavigationBar: Container(
        padding: const EdgeInsets.symmetric(vertical: 12),
        decoration: BoxDecoration(
          color: Colors.white,
          border: Border(top: BorderSide(color: Colors.grey.shade300)),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: const [
            NavItem(icon: Icons.home, label: 'Home'),
            NavItem(icon: Icons.search, label: 'Search'),
            NavItem(icon: Icons.person, label: 'Profile'),
          ],
        ),
      ),
    );
  }
}

class NavItem extends StatelessWidget {
  final IconData icon;
  final String label;

  const NavItem({super.key, required this.icon, required this.label});

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () {
        // Handle tap here
      },
      borderRadius: BorderRadius.circular(12),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(icon, color: Colors.blue),
          const SizedBox(height: 4),
          Text(label, style: const TextStyle(fontSize: 12)),
        ],
      ),
    );
  }
}

This is a basic layout for a bottom navigation bar with evenly spaced icons.

Flutter Row Widget

Common Pitfalls

  • Overflow error: Happens when content doesn’t fit. Use scroll, Expanded, or Flexible.
  • Wrong alignment: Remember mainAxisAlignment is horizontal, and crossAxisAlignment is vertical.
  • Forgetting spacing: Use SizedBox or spacing to avoid widgets looking stuck together.

Conclusion

The Row widget in Flutter is powerful for creating horizontal layouts. It gives you full control over alignment, spacing, sizing, and overflow behavior. Whether you’re building a simple toolbar or a complex layout with dynamic widths, Row can handle it—especially when combined with Expanded, Flexible, and scroll views.

Take time to play with alignment options, flex factors, and spacing to build clean and responsive UIs!

Scroll to Top