Skip to content

Layout: Row & Column

Flutter’s layout model is flexbox — just spelled differently. Row is display: flex; flex-direction: row. Column is display: flex; flex-direction: column. Expanded is flex: 1. MainAxisAlignment maps to justify-content. CrossAxisAlignment maps to align-items.

Once you see the mapping, Flutter layout becomes immediately familiar. The key gotcha is that the main axis flips between Row and Column, whereas in CSS justify-content always means the direction axis you set.

React
/* React / CSS flexbox */
/* Row layout */
.row {
display: flex;
flex-direction: row;
justify-content: space-between; /* main axis */
align-items: center; /* cross axis */
gap: 8px;
}
/* Column layout */
.col {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 12px;
}
/* Flex child that grows */
.grow { flex: 1; }
// JSX
function NavBar() {
return (
<div className="row">
<span className="grow">My App</span>
<button>Login</button>
<button>Sign Up</button>
</div>
);
}
Flutter
// Flutter Row / Column
// Row layout
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 8, // Flutter 3.27+ gap shorthand
children: [/* ... */],
)
// Column layout
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12,
children: [/* ... */],
)
// Flex child that grows (flex: 1)
Expanded(child: someWidget)
// Widget
class NavBar extends StatelessWidget {
const NavBar({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
const Expanded(child: Text('My App',
style: TextStyle(fontWeight: FontWeight.bold))),
TextButton(onPressed: () {}, child: const Text('Login')),
ElevatedButton(onPressed: () {}, child: const Text('Sign 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(
      home: Scaffold(
        appBar: AppBar(title: const Text('Row & Column Demo')),
        body: const Padding(
          padding: EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            spacing: 16,
            children: [
              _SectionLabel('Row — spaceAround'),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  _Chip(label: 'Flutter', color: Colors.blue),
                  _Chip(label: 'Dart', color: Colors.teal),
                  _Chip(label: 'Material', color: Colors.purple),
                ],
              ),
              _SectionLabel('Row with Expanded'),
              Row(
                spacing: 8,
                children: [
                  Expanded(child: _Chip(label: 'Grows to fill', color: Colors.orange)),
                  _Chip(label: 'Fixed', color: Colors.red),
                ],
              ),
              _SectionLabel('Column — center + start'),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                spacing: 6,
                children: [
                  _Chip(label: 'Item 1', color: Colors.indigo),
                  _Chip(label: 'Item 2', color: Colors.indigo),
                  _Chip(label: 'Item 3', color: Colors.indigo),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _SectionLabel extends StatelessWidget {
  final String text;
  const _SectionLabel(this.text);

  @override
  Widget build(BuildContext context) => Text(text,
      style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13,
          color: Colors.grey));
}

class _Chip extends StatelessWidget {
  final String label;
  final Color color;
  const _Chip({required this.label, required this.color});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
      decoration: BoxDecoration(color: color.withOpacity(0.15),
          border: Border.all(color: color),
          borderRadius: BorderRadius.circular(20)),
      child: Text(label, style: TextStyle(color: color, fontWeight: FontWeight.w600)),
    );
  }
}
What is the Flutter equivalent of `justify-content` in CSS flexbox?
For a `Column`, `mainAxisAlignment` controls alignment along which axis?
Which Flutter widget is the equivalent of `flex: 1` on a flex child?
What is the simplest Flutter equivalent of CSS `gap: 8px` between Row children (Flutter 3.27+)?