Rows and Columns

  1. Objectives
  2. Code along
  3. Boilerplate
  4. Layout widgets, Rows, and Columns
  5. Row and Column “alignment” properties
    1. MainAxisAlignment
    2. CrossAxisAlignment
  6. Row and Column size
  7. Conclusion
  8. Other Resources

Objectives

Code along

Recording: https://youtu.be/dcjwErR_f8I

Boilerplate

Create a new Flutter project and replace the starter code in main.dart with the following:

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(title: "Row/Column Demo", home: HomeScreen()));
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Row(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.start,
        children: const [
          Icon(Icons.play_arrow, size: 36),
          Text("Some words"),
          FlutterLogo(size: 96),
        ],
      ),
    );
  }
}

Layout widgets, Rows, and Columns

The term layout refers to how visible widgets are arranged on the screen. In Flutter, layout widgets are how you properly place items on the screen. Layout widgets are invisible themselves – they only arrange their children.

Row and Column are two of the most common layout widget in Flutter apps. A Row displays its children in a horizontal array, and a Column displays its children in a vertical array.

The Row and Column widgets both have a children: property. The children are the other widgets you want to arrange. The children can be visible widgets, such as Buttons, Images, or Texts. The children can also be invisible widgets and other layout widgets, including Row and Column widgets. You can also mix visible widgets with invisible widgets. You can achieve complex layouts by nesting Rows and Columns within one another if you so choose. We will also look at some more high-level layout widgets, such as Grids, in future labs.

Row and Column “alignment” properties

Rows arrange widgets horizontally and Columns stack widgets vertically. You can also control the position of the child widgets within a Row or Column through two properties: mainAxisAlignment and crossAxisAlignment, illustrated below:

mainAxisAlignment and crossAxisAlignment illustrated

The main axis for a Column is the vertical axis and for a Row is the horizontal axis. The cross axis is the other axis: horizontal for Column and vertical for Row.

You control the placement of child widgets within a Row or column by specifying the alignment of each axis. Alignments are similar to “justification” in typesetting - you can be left-justified or right-justified. The alignment properties also have settings for spacing between widgets.

MainAxisAlignment

Row and Column widgets have an optional mainAxisAlignment: property. If you do not set this property, it will have the value MainAxisAlignment.start, which corresponds to the beginning of a Row and the top of a Column. In our boilerplate code, we (redundantly) set mainAxisAlignment: MainAxisAlignment.start,.

The main axis has 6 alignment options:

main axis alignment Row Column
"start" left top
"end" right bottom
"center" horizontal middle vertical middle
"space between" Place the free space evenly between the children.
"space around" Place the free space evenly between the children as well as half of that space before and after the first and last child.
"space evenly" Place the free space evenly between the children as well as before and after the first and last child.

The effects of the various MainAxisAlignment values on a Row are shown below:

MainAxisAlignment.start Row-MainAxisAlignment.start
MainAxisAlignment.center Row-MainAxisAlignment.center
MainAxisAlignment.end Row-MainAxisAlignment.end
MainAxisAlignment.spaceBetween Row-MainAxisAlignment.spaceBetween
MainAxisAlignment.spaceEvenly Row-MainAxisAlignment.spaceEvenly
MainAxisAlignment.spaceAround Row-MainAxisAlignment.spaceAround

Exercise: Change the Row widget to a Column and try the different MainAxisAlignment values. You will see they are visually the same as a Row, except rotate 90° clockwise.

CrossAxisAlignment

Row and Column widgets have an optional crossAxisAlignment: property. You specify this property by saying, for example, crossAxisAlignment: CrossAxisAlignment.center. Here is an excerpt for the Row widget in our boilerplate:

Row(
  mainAxisSize: MainAxisSize.max,
  crossAxisAlignment: CrossAxisAlignment.center,

If you do not set this property, it will default to the value CrossAxisAlignment.start, which corresponds to the top of a Row and the left side of a Column (if viewing in LTR language).

The cross axis has 5 alignment options:

cross axis alignment Row Column
"start" top left
"end" bottom right
"center" vertical middle horizontal middle
"stretch" children fill the space vertically children fill the space horizontally
"baseline" Align children as close to the top as they can be while honoring the baseline alignment. In other words, the extra space will be below all the children. Not used

The effects of the various CrossAxisAlignment values on a Row are shown below. Note that the effects of the cross-axis alignment are obvious here because the child widgets of the Row have vastly different heights. The effects would be less pronounced if the cross axis dimension of the child widgets was the same.

CrossAxisAlignment.start Row-CrossAxisAlignment.start
CrossAxisAlignment.center Row-CrossAxisAlignment.center
CrossAxisAlignment.end Row-CrossAxisAlignment.end

I purposefully do not show the effects of stretch and baseline yet because they are hard to see with our example. We will revisit these options in a future lab.

Exercise: Change the Row widget to a Column and explore the effects of the CrossAxisAlignment property.

Row and Column size

Rows and Columns try to expand to fill all the space along their main axes, but this is impacted by the presence of other widgets (e.g., the IntrinsicWidth widget from the Collage app), nesting, and other factors.

In our boilerplate, we set mainAxisSize: MainAxisSize.max. This tells our Row to expand horizontally as far as possible. This is also the default behavior–you do not have to specify a mainAxisSize.

You can also specify mainAxisSize: MainAxisSize.min. This will tell the Row or Column to shrink as much as possible yet still fit the contents.

Compare the following to see the effects of the mainAxisSize on the mainAxisAlignment. The red outline represents the invisible border of the Row.

Row with max size and space between widgets Row with max size and space between widgets - result
Row with min size and space between widgets Row with min size and space between widgets - result

As you can see, the second option looks like settings MainAxisAlignment.start. In fact, we have shrunk the size of the row. It doesn’t matter what we put for the MainAxisAlignment when the widget size is set to MainAxisSize.min!

The Row and Column widgets do not have any further control over their sizes than the max or min options. The actual size of Row and Column widgets is largely determined by the size of their children, but can also be controlled by parent widgets like IntrinsicWidth and Expanded.

Conclusion

Row and Column widgets are primitive but powerful. Their main role is to arrange items horizontally or vertically. However, the precise positioning of visible widgets depends on the sizes and layout controls of all the widgets on the screen. The algorithm for determining the exact placement on the screen of widgets is quite complex. We will explore more layout options in future labs.

Other Resources