Home Introduction to Flutter UI Layout Widgets on a Screen

📐 Layout Widgets — Arrange Your Widgets Beautifully

⏱️ 35-40 minutes 📊 Beginner — Learn to arrange widgets! 📦 2 Projects included 🏷️ Row, Column, Layout, Alignment, Grid

🧒 What Are Layout Widgets? (The Bookshelf Analogy!)

📚

Imagine Arranging Books on a Shelf...

In the last lesson, you created a single Tile widget. But the Birdle game needs 25 tiles arranged in a 5×5 grid! How do you tell Flutter where to put each tile?

That's where layout widgets come in! They're like bookshelves for your widgets:

↔️

Row

Arranges children horizontally — like books lined up left to right on a single shelf.

↕️

Column

Arranges children vertically — like books stacked top to bottom in a pile.

🔲

Together!

Combine Rows and Columns to create grids — like a whole bookshelf with multiple shelves!

Here's the secret: A grid is just a Column of Rows! Each row is a shelf, and the column stacks those shelves on top of each other.

🎯

Alignment — Telling Widgets WHERE to Sit

When you put widgets in a Row or Column, you need to tell Flutter how to space them out. There are two types of alignment:

📏 MainAxisAlignment

Along the main direction:

  • For a Row (horizontal): controls left↔right positioning
  • For a Column (vertical): controls top↕bottom positioning
start center end spaceBetween spaceEvenly spaceAround

📐 CrossAxisAlignment

Perpendicular to the main direction:

  • For a Row (horizontal): controls top↕bottom positioning
  • For a Column (vertical): controls left↔right positioning
start center end stretch
💡
Easy way to remember:
  • Main = the direction the widgets flow (along the Row/Column)
  • Cross = the opposite direction (across the Row/Column)
  • In a Row: Main is horizontal ↔️, Cross is vertical ↕️
  • In a Column: Main is vertical ↕️, Cross is horizontal ↔️
🔢

The 5×5 Grid — It's Just a Column of 5 Rows!

The Birdle game board is a 5×5 grid. But Flutter doesn't have a "grid" widget (well, it does — GridView — but we're learning the fundamentals first!).

Here's how we build it:

Column
Contains
Row 1
Tile Tile Tile Tile Tile
Row 2
Tile Tile Tile Tile Tile
Row 3
Tile Tile Tile Tile Tile
Row 4
Tile Tile Tile Tile Tile
Row 5
Tile Tile Tile Tile Tile

Column of 5 Rows, each Row has 5 Tiles = 25 Tiles total! That's the entire game board. Simple, right?

1 Start With Your Existing Code

Make sure you have the Tile widget from Lesson 3 and the game.dart file ready. We're going to build on top of that foundation.

Step 1.1 Your Starting Point

Your main.dart should currently have:

  • import 'game.dart'; at the top
  • ✅ The Tile widget class
  • ✅ The MainApp class with a single Tile or a small test layout

If you need to catch up, go back to Lesson 3: Widget Fundamentals.

2 The Row Widget — Arrange Things Horizontally

A Row takes a list of children and places them side by side, from left to right.

Step 2.1 Your First Row — 5 Tiles Across

Let's replace the body of your app with a single Row containing 5 Tiles:

🔍 Create a Row of 5 Tiles

lib/main.dart — Update MainApp.build
@override
Widget build(BuildContext context) {
    return const MaterialApp(
        home: Scaffold(
            body: Center(
                child: Row(                              // ← Row arranges horizontally
                    mainAxisAlignment: MainAxisAlignment.center,  // Center the row
                    children: [
                        Tile('B', HitType.hit),
                        SizedBox(width: 4),
                        Tile('I', HitType.partial),
                        SizedBox(width: 4),
                        Tile('R', HitType.miss),
                        SizedBox(width: 4),
                        Tile('D', HitType.miss),
                        SizedBox(width: 4),
                        Tile('S', HitType.partial),
                    ],
                ),
            ),
        ),
    );
}

What's happening:

  • Row(children: [...]) — The Row widget takes a list of children. It puts them in a line, left to right.
  • mainAxisAlignment: MainAxisAlignment.center — This centers the entire row horizontally on the screen. The tiles will be grouped together in the middle.
  • SizedBox(width: 4) — Adds 4 pixels of empty space between each tile, so they don't touch each other.

Hot reload! You should see 5 colored squares in a horizontal line — B (green), I (yellow), R (gray), D (gray), S (yellow).

Step 2.2 Experiment with MainAxisAlignment

Try changing mainAxisAlignment to these values and hot reload after each to see the difference:

MainAxisAlignment.start

Pushes everything to the left (the start of the row).

🟩 🟨

MainAxisAlignment.center

Groups everything in the middle.

🟩 🟨

MainAxisAlignment.end

Pushes everything to the right.

🟩 🟨

MainAxisAlignment.spaceEvenly

Equal space between tiles AND at the edges.

🟩 🟨

3 The Column Widget — Stack Things Vertically

A Column takes a list of children and places them one below the other, from top to bottom.

Step 3.1 Stack Two Rows Vertically

Now let's wrap our Row inside a Column, and add a second Row below it:

lib/main.dart — Column with 2 Rows
@override
Widget build(BuildContext context) {
    return const MaterialApp(
        home: Scaffold(
            body: Center(
                child: Column(                          // ← Column stacks vertically
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                        // Row 1 — First guess
                        Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                                Tile('B', HitType.hit),
                                SizedBox(width: 4),
                                Tile('I', HitType.partial),
                                SizedBox(width: 4),
                                Tile('R', HitType.miss),
                                SizedBox(width: 4),
                                Tile('D', HitType.miss),
                                SizedBox(width: 4),
                                Tile('S', HitType.partial),
                            ],
                        ),
                        SizedBox(height: 4),  // ← Space between rows
                        // Row 2 — Second guess
                        Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                                Tile('F', HitType.miss),
                                SizedBox(width: 4),
                                Tile('L', HitType.partial),
                                SizedBox(width: 4),
                                Tile('U', HitType.hit),
                                SizedBox(width: 4),
                                Tile('T', HitType.miss),
                                SizedBox(width: 4),
                                Tile('E', HitType.hit),
                            ],
                        ),
                    ],
                ),
            ),
        ),
    );
}

Hot reload! Now you see two rows of 5 tiles each, stacked vertically. The grid is starting to take shape!

🧩
The widget tree for this layout:
MaterialApp → Scaffold → Center → Column → [Row (5 Tiles), SizedBox, Row (5 Tiles)]

Notice how we're nesting widgets: a Column contains Rows, and each Row contains Tiles. This is the core pattern of Flutter layouts!

4 Build the Complete 5×5 Birdle Game Board

Now for the big moment — create all 5 rows with 5 tiles each for a total of 25 tiles!

Step 4.1 The Full 5×5 Grid Code

Here's the complete game board layout. Each row represents one guess attempt:

lib/main.dart — Complete 5×5 Game Board
@override
Widget build(BuildContext context) {
    return const MaterialApp(
        home: Scaffold(
            appBar: AppBar(
                title: Text('Birdle 🐦'),
                centerTitle: true,
            ),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                        // Row 1 — Guess #1
                        Row(mainAxisAlignment: MainAxisAlignment.center, children: [
                            Tile('B', HitType.hit), SizedBox(width: 4),
                            Tile('I', HitType.partial), SizedBox(width: 4),
                            Tile('R', HitType.miss), SizedBox(width: 4),
                            Tile('D', HitType.miss), SizedBox(width: 4),
                            Tile('S', HitType.partial),
                        ]),
                        SizedBox(height: 4),
                        // Row 2 — Guess #2
                        Row(mainAxisAlignment: MainAxisAlignment.center, children: [
                            Tile('F', HitType.miss), SizedBox(width: 4),
                            Tile('L', HitType.partial), SizedBox(width: 4),
                            Tile('U', HitType.hit), SizedBox(width: 4),
                            Tile('T', HitType.miss), SizedBox(width: 4),
                            Tile('E', HitType.hit),
                        ]),
                        SizedBox(height: 4),
                        // Row 3 — Guess #3 (all correct!)
                        Row(mainAxisAlignment: MainAxisAlignment.center, children: [
                            Tile('A', HitType.hit), SizedBox(width: 4),
                            Tile('B', HitType.hit), SizedBox(width: 4),
                            Tile('A', HitType.hit), SizedBox(width: 4),
                            Tile('C', HitType.hit), SizedBox(width: 4),
                            Tile('K', HitType.hit),
                        ]),
                        SizedBox(height: 4),
                        // Row 4 — Empty (no guesses yet)
                        Row(mainAxisAlignment: MainAxisAlignment.center, children: [
                            Tile('', HitType.none), SizedBox(width: 4),
                            Tile('', HitType.none), SizedBox(width: 4),
                            Tile('', HitType.none), SizedBox(width: 4),
                            Tile('', HitType.none), SizedBox(width: 4),
                            Tile('', HitType.none),
                        ]),
                        SizedBox(height: 4),
                        // Row 5 — Empty (no guesses yet)
                        Row(mainAxisAlignment: MainAxisAlignment.center, children: [
                            Tile('', HitType.none), SizedBox(width: 4),
                            Tile('', HitType.none), SizedBox(width: 4),
                            Tile('', HitType.none), SizedBox(width: 4),
                            Tile('', HitType.none), SizedBox(width: 4),
                            Tile('', HitType.none),
                        ]),
                    ],
                ),
            ),
        ),
    );
}
🎉
Hot reload and look at your app! You should see a beautiful 5×5 grid of colored squares — just like a real Wordle-style game! Rows 1-3 show guessed words, rows 4-5 are empty (ready for more guesses).

5 Understanding Your Widget Tree

Let's visualize what you just built — the complete widget tree for the Birdle game board:

🌳 MaterialApp The root of everything
🏠 Scaffold AppBar + Body
📦 Center Centers everything
↕️ Column Stacks 5 rows + spacers
↔️ Row 1
🧩 5 × Tile B,I,R,D,S
↔️ Row 2
🧩 5 × Tile F,L,U,T,E
... Rows 3, 4, 5 follow the same pattern ...
🧠
Key insight: The widget tree is not a flat list. It's a deeply nested structure. Each Row contains 5 Tiles, and the Column contains 5 Rows. This nesting is what gives Flutter its incredible flexibility — you can combine widgets in endless ways!

📝 What You Learned Today

You just learned the most important layout skill in Flutter!

Row Widget

Used Row to arrange 5 Tiles horizontally (side by side) with SizedBox for spacing.

Column Widget

Used Column to stack 5 Rows vertically (one below the other).

MainAxisAlignment

Controlled positioning along the main direction — center, start, end, spaceEvenly.

Built a 5×5 Grid

Combined Column + Rows to create a complete game board with 25 Tile widgets!

🧠 Test Yourself!

Let's check your understanding of layouts:

Q1 If you want to arrange widgets horizontally (side by side), which widget should you use?

Q2 To build a 5×5 grid, you need a Column containing 5 Rows, each with 5 children. What is this pattern called?

📦 Project 1: Create a 3×3 Tic-Tac-Toe Board

Apply your layout skills to build a different grid — a tic-tac-toe board!

Project 1 Beginner

Objective: Build a 3×3 Grid of X's and O's

📋 Requirements:

  1. Create a Column with 3 Rows
  2. Each Row has 3 Tiles (9 tiles total)
  3. Use these letters: Row 1: X, O, X | Row 2: O, X, O | Row 3: X, O, X
  4. All tiles should use HitType.none (white — it's a fresh board)
  5. Add SizedBox(height: 4) between rows and SizedBox(width: 4) between tiles
  6. Center the entire board on screen

🎯 Expected Output:

A 3×3 grid of white tiles showing X and O in a tic-tac-toe pattern, centered on screen.

💡
Hint: The structure is exactly like the Birdle grid, just with 3 rows instead of 5, and 3 tiles per row instead of 5.

📦 Project 2: Build a Number Pad Layout (1-9)

Practice nesting layouts by building a calculator-style number pad!

Project 2 Beginner

Objective: Create a 3×3 Number Grid with a Title

📋 Requirements:

  1. Add a Text title above the grid saying "Number Pad" (bold, size 24)
  2. Below the title, create a 3×3 grid with numbers 1-9
  3. Row 1: 1, 2, 3 | Row 2: 4, 5, 6 | Row 3: 7, 8, 9
  4. All tiles should use HitType.none
  5. Add SizedBox(height: 16) between the title and the grid
  6. The entire layout should be centered

🎯 Expected Output:

A "Number Pad" title centered at the top, with a 3×3 grid of numbered tiles below it, all centered on screen.

💡
Hint: You need a Column that contains: [Text title, SizedBox, another Column of 3 Rows]. Yes — you can nest a Column inside a Column!

🚀 What's Next?

You now know how to arrange widgets using Rows and Columns — the foundation of ALL Flutter layouts! In the next lesson:

  • DevTools — Learn to use Flutter's built-in debugging tools
  • Inspect your widget tree visually
  • Debug layout issues and understand widget sizing
🎉

Lesson Complete!

You built a complete 5×5 game grid using Rows and Columns! You now understand the core layout system that powers every Flutter app.

Click the button above to track your progress!