Flutter (25): Flexible layout (Flex)

Time: Column:Mobile & Frontend views:186

A flexible layout allows child components to allocate the parent container's space according to certain proportions. The concept of flexible layout exists in other UI systems as well, such as the flexible box layout in H5 or FlexboxLayout in Android. In Flutter, flexible layouts are mainly achieved through the combination of Flex and Expanded.

25.1 Flex

The Flex component can arrange its child components either horizontally or vertically. If you already know the main axis direction, it’s easier to use Row or Column since both Row and Column inherit from Flex, and their parameters are essentially the same. Therefore, anywhere you can use Flex, you can also use Row or Column. The Flex widget is quite powerful on its own and can be used with Expanded to achieve flexible layouts. In the following section, we’ll focus only on the properties related to flexible layouts (the other properties were introduced when discussing Row and Column).

Flex({
  ...
  required this.direction, // The direction of the flexible layout. For Row, it defaults to horizontal; for Column, it defaults to vertical.
  List<Widget> children = const <Widget>[],
})

Flex inherits from MultiChildRenderObjectWidget, and its corresponding RenderObject is RenderFlex, which implements its layout algorithm.

25.2 Expanded

Expanded can only be a child of Flex (otherwise, an error will occur), and it allows the child component of Flex to “expand” and occupy space proportionally. Since Row and Column both inherit from Flex, Expanded can also be their child.

const Expanded({
  int flex = 1,
  required Widget child,
})

The flex parameter is the elasticity factor. If it’s 0 or null, the child will not be flexible, meaning it won’t expand to occupy any extra space. If it's greater than 0, all Expanded children will divide the available space along the main axis according to their flex ratio. Let’s look at an example:

class FlexLayoutTestRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        // The two child widgets of Flex occupy the horizontal space in a 1:2 ratio
        Flex(
          direction: Axis.horizontal,
          children: <Widget>[
            Expanded(
              flex: 1,
              child: Container(
                height: 30.0,
                color: Colors.red,
              ),
            ),
            Expanded(
              flex: 2,
              child: Container(
                height: 30.0,
                color: Colors.green,
              ),
            ),
          ],
        ),
        Padding(
          padding: const EdgeInsets.only(top: 20.0),
          child: SizedBox(
            height: 100.0,
            // The three child widgets of Flex occupy the 100 pixels of vertical space in a 2:1:1 ratio
            child: Flex(
              direction: Axis.vertical,
              children: <Widget>[
                Expanded(
                  flex: 2,
                  child: Container(
                    height: 30.0,
                    color: Colors.red,
                  ),
                ),
                Spacer(
                  flex: 1,
                ),
                Expanded(
                  flex: 1,
                  child: Container(
                    height: 30.0,
                    color: Colors.green,
                  ),
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }
}

The running result is shown in Figure .

Flutter (25): Flexible layout (Flex)

In the example, the Spacer widget’s function is to occupy space according to a specified proportion. In reality, it’s just a wrapper class for Expanded. Below is the source code for Spacer:

class Spacer extends StatelessWidget {
  const Spacer({Key? key, this.flex = 1})
    : assert(flex != null),
      assert(flex > 0),
      super(key: key);
  
  final int flex;

  @override
  Widget build(BuildContext context) {
    return Expanded(
      flex: flex,
      child: const SizedBox.shrink(),
    );
  }
}

25.3 Summary

The flexible layout is relatively simple. The only thing to note is the relationship between Row, Column, and Flex.