Flutter Widget Positioning - A Guide for the CSS Developer

The addition of Flexbox to CSS revolutionized the way we position elements on the web. It makes the alignment of elements on a 2D plane automatic and well, flexible. Fortunately, Flutter uses the same principles for aligning widgets with Flex, Column, and Row. In addition, it supports absolute and fixed positioning with Stack.

The following lesson will teach you how to position containers and build layouts in Flutter with direct comparisons to CSS Flexbox.

Throughout this lesson, you will see the 👉 emoji. Flutter will always be on the left, and CSS on the right.

Make sure to also watch Flutter for the JavaScript Developer.

Basic Positioning for Single Widgets

Do you need to position one widget or multiple widgets? If you answered one, you likely want to wrap your widget with a Container.

Container

Containers serve as an all-purpose wrapper for positioning and styling widgets. They are similar to an HTML div in the sense that they serve as blank UI elements that can be positioned/styled. Below is a list of the most common positioning options.

file_type_dartlang main.dart
Container( 
    width: 100,
    height: 100,
    margin: EdgeInsets.all(24),
    padding: EdgeInsets.only(top: 24),
    alignment: Alignment.center,
    transform: Transform.rotate(...),
    child: MyCoolWidget()
)
basic margin and padding on flutter widget

The equivalent HTML/CSS would look something like this:

file_type_css style.css
.my-cool-widget {
    width: 100px;
    height: 100px;
    margin: 24px;
    padding-top: 24px;
    text-align: center; 
}
/*  <div class="my-cool-widget"></div>  */

Padding, Center, Align, and More

In many cases, you will not need the full kitchen-sink of features in a container. For example, when you only need to provide padding to a widget, consider using the Padding widget, or if you just want to center a widget wrap it in Center. It will make your code more readable and succinct.

file_type_dartlang main.dart
Padding( 
    padding: EdgeInsets.all(24),
    child: MyCoolWidget()
)

Center( 
    child: MyCoolWidget()
)

CSS Flexbox vs Flutter Rows and Columns

A Basic Row & Column

Rows and Columns are identical in every way, just with their axis flipped.

row of three boxes in Flutter
column of three boxes in Flutter
file_type_dartlang main.dart
Column(
    children: [
        MyWidget(),
        MyWidget(),
        MyWidget()
    ]
);

// or swap a Column for a Row to flip the axis

Row(children: [ ]);

// and this is all just sugar for the Flex widget
Flex(
    direction: Axis.vertical
)

MainAxis vs CrossAxis

  • mainAxisAlignment 👉 align-items;
  • crossAxisAlignment 👉 justify-content;

In Flutter, you might find yourself asking should I set the mainAxisAlignment or crossAxisAlignment. The answer depends whether you’re aligning a Row or Column.

The MainAxis runs along the flow or direction of the widgets, so X-axis for Rows and Y-axis for Columns. The CrossAxis is the opposite.

On the main-axis, you control the spacing between the widgets. On the cross-axis, you control the alignment within the parent container.

file_type_dartlang main.dart
Column(
    mainAxisAlignment: MainAxisAlignment.spaceAround,
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
        MyWidget(),
        MyWidget(height: 200.0, width: 200.0),
        MyWidget(height: 50.0, width: 50.0)
    ]
);
aligned boxes Flutter

In CSS we can achieve the same effect with a flex row or column.

file_type_css style.css
.row {
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    align-items: flex-start;
}

Expanded

Sometimes you need to specify flex behavior from a child widget. The Expanded widget will take up the remaining space of a row or column. It takes an optional flex argument to control the ratio of space used when working with multiple expanded widgets.

Notice how the middle widget has a flex of 3, while the last widget has a flex of 1. This means the middle widget will take up 3x more space. In other words, it will expand to 75% of the available space, while the last widget will take up the remaining 25%.

file_type_dartlang main.dart
Column(
    children: [
        MyWidget(),
        Expanded(
            flex: 3,
            child: MyWidget(),
        ),
        Expanded(
            flex: 1,
            child: MyWidget()
        )
    ]
);
}
expanded flex widget in Flutter

The CSS equivalent:

file_type_css style.css
.column {
    display: flex;
    flex-direction: column;
}

.expanded1 {
    flex-grow: 1;
}

.expanded3 {
    flex-grow: 3;
}

Stacks vs Absolute/Fixed Positioning

A Stack allows you to stack elements on top of each other, with the last element in the array taking the highest prority. You can use Align, Positioned or Container to position the children of a stack.

Align

Widgets are moved by setting the alignment with Alignment, which has static properties like topCenter, bottomRight, and so on. Or you can take full control and set Alignment(1.0, -1.0), which takes x,y values ranging from 1.0 to -1.0, with (0,0) being the center of the screen.

file_type_dartlang main.dart
Stack(
    children: [
        MyWidget(),
        Align(
            alignment: Alignment.topCenter,
            child: MyWidget(),
        ),
        Container(
            alignment: Alignment(-0.9, -0.9),
            child: MyWidget(),
        )  
    ]
);
aligned boxes Flutter

Positioned

As an alternative to align, you can position children relative to the parent widget.

file_type_dartlang main.dart
Stack(
    children: [
        MyWidget(),
        Positioned(
            bottom: 20,
            left: 20,
            child: MyWidget(color: Colors.blue),
        ),
        Positioned(
            top: 50,
            right: 50,
            child: MyWidget(color: Colors.red)
        )
    ]
)
positioned boxes Flutter

And with CSS absolute positioning.

file_type_css style.css
.positioned {
    position: absolute;
    bottom: 20,
    left: 20,
}

The End

As you can see, CSS shares many of the same positioning and alignment concepts with Flutter. In 99% of cases, you can solve your positioning requirements with Container, Column, Row, and Stack. The beauty of Flutter is that you have the power to paint every pixel on the device when needed in more advanced scenarios.

Questions? Let's chat

Open Discord