Chinedu Imoh Chinedu is a tech enthusiast focused on full-stack JavaScript and Infrastructure engineering.

Create responsive layouts with Flexible and Expanded widgets in Flutter

4 min read 1368

Building responsive screen layouts means writing a single piece of code that responds to various changes in the device’s layout, so the app displays its UI according to the screen size and shape of the device.

In this article, we will explore Expanded and Flexible widgets in Flutter for screen responsiveness.

Due to Flutter’s cross-platform, single codebase ability, it is essential to understand screen management to prevent problems like the flex overflow errors or bad user interface design.

We will also design a demo of Expanded and Flexible widgets, and describe their properties and how to use them in a Flutter application.

Prerequisites

To understand and follow along with this tutorial, you should have the following:

  • Flutter installed on your local machine
  • Working knowledge of Flutter and Dart

Problems with using containers

In Flutter, a container is a parent widget containing multiple child widgets. It manages them through width, height, background color, and padding, among other descriptors. Basically, a container is a box into which we can pass content.

There are two reasons why creating a responsive screen layout in Flutter using containers for content is not advisable.

First is a RenderFlex overflow. This is one of the most frequently encountered Flutter framework errors; when it happens, you’ll see yellow and black stripes indicating the area of overflow in the app UI, in addition to the error message in the debug console.

“Content undersize for large screens” is simply a UI error in which the content is too small or too large for a particular screen due to Flutters’ flexibility.

Both of these problems can be resolved using Flexible or Expanded widgets, providing a better UI and dev experience.

We made a custom demo for .
No really. Click here to check it out.

Introduction to the Expanded widget

The Expanded widget is a single child widget, meaning that only one child can be assigned to it. For better optimization, it is used in a row or columns.

Properties of the Expanded widget include the child widget and the flex widget.

The child widget is placed inside an Expanded widget, which can take in rows and columns. Flex is used to distribute the contents of the child widget unevenly.

In the code below, we use the Expanded widget with flex set to 1, and a regular container showing the effect of the Expanded widget and its properties:

Expanded(
 flex: 1,
 child: Container(
 color: Colors.red,
 ),
),

Introduction to the Flexible widget

Flexible widgets are pretty similar to Expanded widgets, but the significant difference is in their properties. The Flexible widget is used to adjust the child’s content location within the screen.

Properties of Flexible widget include fit and flex.

Fit controls how the property fills the available space. It has two options: FlexFit.Tight, which sets it to fill the available space, and FlexFit.loose, which fills the remaining available space of the child widget.

Like in the Expanded widget, flex is used to distribute the contents of the child widget unevenly.

The code below uses a Flexible widget with the flex set to 1, fit as FlexFit.loose, and a child container with a regular feature:

Flexible(
   flex: 1,
   fit: FlexFit.loose,
   child: Container(
   height: 100,
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(15),
      color: Colors.deepOrange[400],
      ),
    child:Icon(Icons.backpack),
 ),
),

Setting up a sample application

In this demo, we will create a sample Flutter app with a layout for content displayed in rows and columns.

Here’s a gif showcasing the demo app we will build in this post:

Gif of sample Flutter app with responsive layout

Let’s start with creating a Flutter project directory; enter the following command into your terminal:

mkdir FlutterApps

Next, create a Flutter project:

flutter create sample_app

Now, open the Flutter project in any code editor of your choice.

Code implementation

Paste the following code into the main.dart file. We start by creating a stateful widget called homepage.

Inside homepage, we will create two buttons that direct us to two different screens to see the difference in using Expanded and Flexible widgets in screen layout:

Scaffold(
  body: Center(
    child:
      Column(mainAxisAlignment: MainAxisAlignment.center, children: [
 GestureDetector(
  onTap: () {
   Navigator.push(
    context,
    MaterialPageRoute(
     builder: (context) => ExpandedWidget(),
    ),
   );
  },
  child: Container(
    height: 50,
    width: 150,
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(10), color: Colors.red),
    child: Center(child: Text("Expanded Widget"))),
 ),
 SizedBox(height: 100),
 GestureDetector(
  onTap: () {
   Navigator.push(
    context,
    MaterialPageRoute(
     builder: (context) => FlexibleWidget(),
    ),
   );
  },
  child: Container(
    height: 50,
    width: 150,
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(10),
      color: Colors.teal[700]),
    child: Center(child: Text("Flexible Widget"))),
 )
])));

The buttons created are simple containers with some decoration, color, and text widgets wrapped up with a gesture detector, enabling us to use the onTap property to route to ExpandedWidget() and FlexibleWidget() screens.

Expanded Widget example

Start with creating a file called expanded.dart:

touch expanded.dart

Next, paste the following code into the file. In the code, we created a stateless widget to write our examples using the flex property:

class ExpandedWidget extends StatelessWidget {
 const ExpandedWidget({Key? key}) : super(key: key);
 @override
 Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
     leading: GestureDetector(
       onTap: () {
        Navigator.pop(context);
       },
       child: Icon(Icons.arrow_back_ios_new)),
    ),
    body: Padding(
     padding: const EdgeInsets.symmetric(horizontal: 15),
     child: Column(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: [
       Column(
        children: [
         Text("With Flex"),
         Container(
          height: 100,
          child: Row(
           children: [
            Expanded(
             flex: 1,
             child: Container(
              color: Colors.red,
             ),
            ),
            Expanded(
             flex: 2,
             child: Container(
              color: Colors.deepOrange[400],
             ),
            ),
            Expanded(
             flex: 3,
             child: Container(
              color: Colors.purpleAccent,
             ),
            )
           ],
          ),
         ),
        ],
       ),
       Column(
        children: [
         Text("Without Flex"),
         Container(
          height: 100,
          child: Row(
           children: [
            Expanded(
             child: Container(
              color: Colors.red,
             ),
            ),
            Expanded(
             child: Container(
              color: Colors.deepOrange[400],
             ),
            ),
            Expanded(
             child: Container(
              color: Colors.purpleAccent,
             ),
            )
           ],
          ),
         ),
        ],
       ),
      ],
     ),
    ));
 }
}

First, we return a scaffold so that we can use the appbar and body properties. Next, in the appbar, we created a back button so we can return to the previous screen.

Moving on to the body, we use two columns, one at the top and another at the button spacing them out; in each column, we have a text describing if it’s with or without flex. Under it, we created a row using three Expanded widgets with or without flex and a container assigning different colors.

The image below shows the layout with and without flex applied:

iPhone displaying a sample of the expanded widget with and without the flex property

Flexible Widget example

Start by creating a file called flexible.dart:

touch flexible.dart

Next, paste the following code into the file:

class FlexibleWidget extends StatelessWidget {
 const FlexibleWidget({Key? key}) : super(key: key);
 @override
 Widget build(BuildContext context) {
  return Scaffold(
   appBar: AppBar(
    leading: GestureDetector(
      onTap: () {
       Navigator.pop(context);
      },
      child: Icon(Icons.arrow_back_ios_new)),
   ),
   body: Padding(
    padding: const EdgeInsets.symmetric(horizontal: 10),
    child: Column(
     mainAxisAlignment: MainAxisAlignment.spaceAround,
     children: [
      Column(
       children: [
        Text("Flexfit.loose"),
        Row(
         mainAxisAlignment:MainAxisAlignment.center,
         children: [
          Flexible(
           flex: 1,
           fit: FlexFit.loose,
           child: Container(
            height: 100,
            decoration: BoxDecoration(
             borderRadius: BorderRadius.circular(15),
             color: Colors.deepOrange[400],
            ),
            child:Icon(Icons.backpack),
           ),
          ),
          SizedBox(
           width: 10,
          ),
          Flexible(
           flex: 1,
           fit: FlexFit.loose,
           child: Container(
            height: 100,
            decoration: BoxDecoration(
             borderRadius: BorderRadius.circular(15),
             color: Colors.deepOrange[400],
            ),
            child:Icon(Icons.backpack),
           ),
          )
         ],
        )
       ],
      ),
      Column(
       children: [
        Text("Flexfit.tight"),
        Row(
         children: [
          Flexible(
           flex: 1,
           fit: FlexFit.tight,
           child: Container(
             height: 100,
           decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(15),
              color: Colors.purpleAccent,
           ),
           child:Icon(Icons.backpack),
           ),
          ),
          SizedBox(
           width: 10,
          ),
          Flexible(
           flex: 1,
           fit: FlexFit.tight,
           child: Container(
             height: 100,
            decoration: BoxDecoration(
             borderRadius: BorderRadius.circular(15),
             color: Colors.purpleAccent,
            ),
            child:Icon(Icons.backpack),
           ),
          )
         ],
        )
       ],
      )
     ],
    ),
   ),
  );
 }
}

In the code, we created a stateless widget, FlexibleWidget. Inside it, we created two rows with Flexible widget content. In the first row, we use flexfit.loose, and in the second, we use flexfit.tight. With that, the icon will fill the available space provided by the child.

The image below shows the layout with flexfit.loose using the bare minimum space provided by the child and flexfit.tight filling the available space provided by the child.

iPhone demonstration of flexible widget with flexfittight and flexfitloose

The difference between the Expanded and Flexible widgets

Like I pointed out earlier, the major difference between these widgets lies in their properties. The Expanded widget only has child and flex properties, which could be a limitation if misused. In contrast, the Flexible widget has more properties; that makes the usage flexible, hence the name.

Conclusion

In this article, we learned about responsive screen layout using Expanded and Flexible widgets. We started by covering potential problems bound to occur when using containers to create a responsive screen layout, then introduced the solutions: the Expanded and Flexible widgets. We covered their properties, similarities, differences, and most importantly, a hands-on example. I hope this post was helpful. Note there is no learning without practice, so make sure to keep practicing.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Chinedu Imoh Chinedu is a tech enthusiast focused on full-stack JavaScript and Infrastructure engineering.

Leave a Reply