Skip to content
Boring Doc
Official documentation (snapshot)doc

Layout

Learn about common layout widgets in Flutter.

Source last updated Oct 21, 2026 · last synchronized 3 weeks ago · commit b3045439b7c0 · read on docs.flutter.dev

This is a rendered snapshot of the official page as stored by Boring Doc for provenance and search. Site-specific components (embedded demos, DartPad, images with relative paths) may not render; use the official page for the full experience. Content © The Flutter Authors, CC BY 3.0.

Learn how to build layouts with common widgets like Scaffold, AppBar, Column, and Row.


Introduction

Given that Flutter is a UI toolkit, you'll spend a lot of time creating layouts with Flutter widgets.

In this section, you'll learn how to build layouts with some of the most common layout widgets. This includes high-level widgets like Scaffold and AppBar, which lay out the structure of a screen, as well as lower-level widgets like Column or Row that lay out widgets vertically or horizontally.

Scaffold and AppBar

Mobile applications often have a bar at the top called an "app bar" that can display a title, navigation controls, and/or actions.

The simplest way to add an app bar to your app is by using two widgets: Scaffold and AppBar.

Scaffold is a convenience widget that provides a Material-style page layout, making it simple to add an app bar, drawer, navigation bar, and more to a page of your app. AppBar is, of course, the app bar.

Add an AppBar to the Scaffold widget within your MainApp widget's build method. The following code uses an additional layout widget: Align. This positions the title to the left, which would be centered by default. The Text widget contains the title itself.

Passing an enum or static property directly (like Alignment.centerLeft) can also be shortened using Dart's dot shorthands syntax, which you can read more about on both the official Dart documentation and the Flutter shorthands overview.

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Align(
            alignment: Alignment.centerLeft,
            child: Text('Birdle'),
          ),
        ),
        body: const Center(
          child: Tile('A', HitType.hit),
        ),
      ),
    );
  }
}

An updated widget tree

Considering your app's widget tree gets more important as your app grows. At this point, there's a "branch" in the widget tree for the first time, and it now looks like the following figure:

Create a widget for the game page layout

Add the following code for a new widget, called GamePage, to your main.dart file. This widget will eventually display the UI elements needed for the game itself.

class GamePage extends StatelessWidget {
  GamePage({super.key});
  // This object is part of the game.dart file.
  // It manages wordle logic, and is outside the scope of this tutorial.
  final Game _game = Game();

  @override
  Widget build(BuildContext context) {
    // TODO: Replace with screen contents
    return Container();
  }
}

Then update your MainApp widget to create and display a GamePage widget instead of the Tile widget.

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Align(
            alignment: Alignment.centerLeft,
            child: Text('Birdle'),
          ),
        ),
        body: Center(child: GamePage()),
      ),
    );
  }
}

Because GamePage initializes a non-constant field (final Game _game = Game();), its constructor cannot be const. If you previously had const Center(...) or const MaterialApp(...), Dart reports an error: "The constructor being called isn't a const constructor." To resolve this, remove the const keyword from Center (or MaterialApp) (or the enclosing parent widget) and only use const on widgets with constant arguments, like const Align(...).

The GamePage layout contains the grid of tiles that display a user's guesses.

There are a number of ways you can build this layout. The simplest is with the Column and Row widgets. Each row contains five tiles that represent the five letters in a guess, with five rows total. So you'll need a single Column with five Row widgets as children, where each row contains five children.

To get started, replace the Container in GamePage.build with a Padding widget with a Column widget as its child:

class GamePage extends StatelessWidget {
  GamePage({super.key});
  // This manages game logic, and is out of scope for this lesson.
  final Game _game = Game();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        spacing: 5.0,
        children: [
          // Add children next.
        ],
      ),
    );
  }
}

The spacing property puts five pixels between each element on the main axis.

Within Column.children, for each element in the _game.guesses list, add a Row widget as a child.

elements, one for each potential guess. The list will always contain exactly five elements, and therefore will always render five rows.

class GamePage extends StatelessWidget { GamePage({super.key}); // This manages game logic, and is out of scope for this lesson. final Game _game = Game();

@override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(8.0), child: Column( spacing: 5.0, children: [ for (final guess in _game.guesses) Row( spacing: 5.0, children: [ // We'll add the tiles here later. ], ), ], ), ); } }


The `for` loop in the `children` list is called a [collection for element][],
a Dart syntax that allows you to iteratively add items to a collection
when it is built at runtime.
This syntactic sugar makes it easier for you to work
with collections of widgets,
providing a declarative alternative to the following:

```dart
[..._game.guesses.map((guess) => Row(/* ... */))],

In this case, it adds five Row widgets to the column, one for each guess on the Game object.

An updated widget tree

The widget tree for this app has expanded significantly in this lesson. Now, it looks more like the following (abridged) figure:

Add a Tile to each row for each letter allowed in the guess. Each element in guess is a record with the type ({String char, HitType type}).

Use a nested loop to iterate over the letters in each guess.

Solution:

class GamePage extends StatelessWidget {
  GamePage({super.key});

  // This manages game logic, and is out of scope for this lesson.
  final Game _game = Game();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        spacing: 5.0,
        children: [
          for (final guess in _game.guesses)
            Row(
              spacing: 5.0,
              children: [
                for (final letter in guess) Tile(letter.char, letter.type),
              ],
            ),
        ],
      ),
    );
  }
}

Review

Test yourself

Was this useful?

Content from flutter/website, © The Flutter Authors, used under the Creative Commons Attribution 3.0 License; code samples under the BSD License.