Responsive Flutter layout with Expanded widget

Key takeaways:

  • The Expanded widget in Flutter helps a child widget take up extra space within a layout. It stretches the child to fill available space in the direction you’re arranging the widgets (either horizontally or vertically).

  • Wrapping a child widget with Expanded allows it to occupy all available space or a specified proportion using the flex property.

  • Without flex, all children in an Expanded widget share space equally. Adding flex values assigns space proportionally.

  • The Expanded widget is versatile and can be used in both web and mobile applications.

  • The expandedWithFlex() and expandedDefault() methods showcase how Expanded widgets allocate space differently with and without flex values.

Responsive layouts are a cornerstone of modern app development, and Flutter makes this easier with the Expanded widget. This widget is designed to dynamically allocate space among child widgets within a Row or Column, making your app adaptable to different screen sizes. To customize how much space each widget occupies, you can use the flex property. This helps resolve any competition for space by assigning different proportions to the children. In this Answer, we’ll explore how the Expanded widget works through two examples: its default behavior and the use of the flex property to customize space distribution.

If you're eager to master Flutter layouts, dive into our Answer: Layouts in Flutter and unlock the secrets to building dynamic and responsive UIs.

Example 1: The default behavior of Expanded

The Expanded widget is a layout widget that can only have one child assigned to it. In the following example, the Row widget contains three children created using the childWidget() function, each wrapped in an Expanded widget. By default, all children expand equally to fill the available horizontal space, as the main axis is horizontal.

Row(
children: [
Expanded(
child: Container(
color: Colors.red,
child: const Center(child: Text("Child 1")),
),
),
Expanded(
child: Container(
color: Colors.green,
child: const Center(child: Text("Child 2")),
),
),
Expanded(
child: Container(
color: Colors.blue,
child: const Center(child: Text("Child 3")),
),
),
],
)
Expanded widget code

This code creates a Row with three Expanded children, each sharing equal space within the row. Each child widget is wrapped in a container with a unique background color and a centered text label. The layout automatically distributes the available horizontal space evenly among the children.

Example 2: Customizing space with the flex property

In this example, each Expanded widget is provided with a flex value. This helps resolve any competition for space by specifying how much space each child should take relative to the others.

Row(
children: [
Expanded(
flex: 4,
child: Container(
color: Colors.red,
child: const Center(child: Text("Flex 4")),
),
),
Expanded(
flex: 3,
child: Container(
color: Colors.green,
child: const Center(child: Text("Flex 3")),
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.blue,
child: const Center(child: Text("Flex 1")),
),
),
],
)
Expanded widget with flex property code

This code defines a Row with three Expanded children, each taking up space proportional to their flex values: 4, 3, and 1. The containers, with distinct colors and text labels, adjust their widths dynamically based on the assigned proportions, creating a responsive layout.

Source code

Note: We can use this code for both web and android applications. But in this section, we are only using the flutter web application for the visual demonstration.

Click the ”Run” button to execute the application.

import 'package:flutter/material.dart';

void main() => runApp(ExpandedDemo());

class ExpandedDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: MyExpanded(),
    );
  }
}

class MyExpanded extends StatefulWidget {
  @override
  _MyExpandedState createState() => _MyExpandedState();
}

int noFlex = 0;
int withFlex = 1;

Map<int, String> dropdown = {
  noFlex: "No `flex` Property",
  withFlex: 'Using `flex`',
};

class _MyExpandedState extends State<MyExpanded> {
  int _currentOption = 0;
  String dropDownValue = dropdown[0];
  bool isFlex = false;

  @override
  void initState() {
    super.initState();
    updateContainer(0);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Expanded Widget"),
        actions: [
          Padding(
            padding: const EdgeInsets.only(left: 16.0),
            child: DropdownButton(
              hint: dropDownValue == null
                  ? Text('Select')
                  : Text(dropDownValue),
              items: dropdown.keys
                  .map((e) => DropdownMenuItem(
                        child: Text(dropdown[e]),
                        onTap: () {
                          setState(() {
                            _currentOption = e;
                            updateContainer(
                                _currentOption == 0 ? noFlex : withFlex);
                          });
                        },
                        value: e,
                      ))
                  .toList(),
              onChanged: (newValue) {
                dropDownValue = dropdown[newValue];
              },
            ),
          ),
        ],
      ),
      body: isFlex ? expandedWithFlex() : expandedDefault(),
    );
  }

  Widget expandedWithFlex() {
    return Row(
      children: [
        Expanded(
          flex: 4,
          child: Container(
            color: Colors.red,
            child: const Center(child: Text("Flex 4")),
          ),
        ),
        Expanded(
          flex: 3,
          child: Container(
            color: Colors.green,
            child: const Center(child: Text("Flex 3")),
          ),
        ),
        Expanded(
          flex: 1,
          child: Container(
            color: Colors.blue,
            child: const Center(child: Text("Flex 1")),
          ),
        ),
      ],
    );
  }

  Widget expandedDefault() {
    return Row(
      children: [
        Expanded(
          child: Container(
            color: Colors.red,
            child: const Center(child: Text("Child 1")),
          ),
        ),
        Expanded(
          child: Container(
            color: Colors.green,
            child: const Center(child: Text("Child 2")),
          ),
        ),
        Expanded(
          child: Container(
            color: Colors.blue,
            child: const Center(child: Text("Child 3")),
          ),
        ),
      ],
    );
  }

  void updateContainer(int option) {
    setState(() {
      isFlex = option == withFlex;
    });
  }
}
Using Expanded widget with and without flex property

Once the server is up and running, please open the app using the URL provided at “Your app can be found at.”

Explanation

Let’s understand how the code above works by breaking it down:

  • Line 1: We import the package:flutter/material.dart package to use the pre-built design widgets for Flutter.

  • Lines 5–13: We define a stateless widget class named ExpandedDemo that returns a MaterialApp widget in its build() method for building material design apps, such as a theme, navigation, and accessibility support.

  • Lines 15–18: We define a stateful widget class named MyExpanded that can change its state during its lifetime.

  • Lines 75–101: We defined an expandedWithFlex() method that returns a Row widget with three child widgets wrapped under an Expanded widget. Each widget has a property set, which determines how much space it should occupy along the main axis of its parent widget.

  • Lines 103–126: We defined an expandedDefault() method that returns a Row widget with three child widgets, which are wrapped under an Expanded widget. Each widget is given a child widget by calling the function with an empty string as the argument. The widget allows the child widget to expand and fill the available space along the main axis of the Row.

When to use the Expanded widget

  • Dynamic space allocation: Use the Expanded widget when you want child widgets within a Row, Column, or Flex to automatically expand and fill the available space along the main axis.

  • Equal space distribution: It’s ideal when all child widgets share the space equally, making it great for simple, balanced layouts.

  • Quick layout adjustments: If you don’t need to customize the space allocation of each child, Expanded provides a quick solution for flexible layouts without too much complexity.

However, if you need more control over how much space each child occupies:

  • For more control: While Expanded forces a child widget to take up all available space, the Flexible widget allows child widgets to take only as much space as they need while still being able to expand if space is available. Consider the Flexible widget, which allows you to assign specific proportions of space to child widgets, offering more flexibility compared to Expanded.

  • Granular adjustments: Use Flexible when you need child widgets to share space in a non-equal way, based on their assigned flex values.

Best practices for the Expanded widget

  1. Combine with other widgets: Use Expanded with Flexible or Spacer widgets for more control over spacing and layout.

  2. Keep it simple: Avoid overcomplicating designs with nested Expanded widgets.

  3. Test on different screens: Always test your layout on various devices to ensure responsiveness.

Benefits of using Expanded widget

  • Automatic space distribution: Adjusts child sizes dynamically based on available space.

  • Cleaner layouts: Simplifies the process of creating flexible, adaptable designs.

  • Code efficiency: Reduces the need for manual size calculations.

Conclusion

The Expanded widget in Flutter is a powerful tool for creating responsive layouts by distributing available space among child widgets within a Row or Column. By using the flex property, developers can control how much space each child occupies, enabling precise and flexible design adjustments. This functionality makes the Expanded widget essential for building adaptable user interfaces in both web and mobile Flutter applications. Through examples and practical implementation, this Answer highlights how to effectively utilize Expanded widgets to achieve dynamic and proportional layouts.

Unlock your full potential with our comprehensive course: Beginning Flutter: Android Mobile App Development. Dive deeper into the topic and gain hands-on experience through expert-led lessons.

Frequently asked questions

Haven’t found what you were looking for? Contact Us


What is the `Expanded` widget?

The Expanded widget is used within a Row, Column, or Flex to distribute available space among its children, either equally or proportionally using the flex property.


How to make Flutter app responsive according to different screen sizes?

Use widgets like Expanded, Flexible, and MediaQuery to adapt layouts to screen size. Leverage LayoutBuilder and responsive design principles to adjust UI elements dynamically.


Can we create responsive layouts using the `Flexible` widget?

Yes, the Flexible widget in Flutter allows you to create responsive layouts by adjusting the space occupied by children within a Row, Column, or Flex based on available space and the flex factor.


Free Resources