Creating Responsive Layouts Part II: Exploring Layout Builder in Flutter
A Deep Dive into Layout Builder: Creating Responsive Layouts in Flutter
Note: This blog assumes that you are using vscode, Android Studio or IntelliJ as IDE.
This is the second part of the Creating Responsive Layouts series and focuses on Layout Builders. The first part was about Media Query and can be found here.
In this blog we will be exploring Layout Builder and understand how it contributes in creating Responsive Layouts in Flutter.
- What is Layout Builder?
- How to use Layout Builder?
- Creating Responsive layouts using Layout Builder
- Conclusion
So, without a further ado lets get started.
What is Layout Builder?
Layout Builder is an amazing widget in flutter which provides information about the constraints that are enforced by the parent widget. As a beginner, one might mix up constraints and size, but there is a difference.
BoxConstraints or constraints are basically a set of four values minimum and maximum width and minimum and maximum height which are padded down by the parent widget, whereas Size refers to the acthual dimensions of the widget after the layout has been rendered. Size of a widget cannot be bigger or smaller than maximum and minimum constraints.
So, to sum it up we can say that,
A widget can have a any “size” as long as it fits within the “constraints”, which are enforced by the parent widget.
This is were LayoutBuilder comes in. See, LayoutBuilder is the widget which informs about those incoming constraints. Once we know what the constraints are, we can then craft the layout to fit the available space in an efficient way.
BoxConstraints, that is everything LayoutBuilder has to offer, there is nothing more. Next up let’s discuss how to use LayoutBuilder.
How to use Layout Builder?
Using LayoutBuilder must be eazy, just give it a child…… wait, wait, wait…hold on, LayoutBuilder does not have a child property as rest of the widgets.
As the name suggests it is a Builder widget and instead of child it has a required builder property, this builder property is acthually a Widget Function that takes two required parameters, BuildContext and BoxConstraints and then returns a Widget. This is how we to do it
import 'package:flutter/material.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: LayoutBuilderExample()); //Used LayoutBuilderExample() as home
}
}
class LayoutBuilderExample extends StatelessWidget {
const LayoutBuilderExample({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder( //Used Layout Builder
builder: (context, constraints) { //This is how we implement builder function
return Container( //The widget returned by builder function
width: constraints.maxWidth, //set the width based on constraints
height: constraints.maxHeight, //set the height based on constraints
color: Colors.purple,
);
},
);
}
}In the above code we have defined a StatelessWidget called as LayoutBuilderExample, used as home, whose build method returns a LayoutBuilder. In the builder function we have specified two parameters BuildContext context and BoxConstraints constraints, which are the constraints passed down by the parent widget. In our case the parent widget is the device screen itself so constraints will be same as screen dimensions. Next, the LayoutBuilder returns a purple Container whose dimensions are based on constraints incoming from the parent widget. We have specified the width and height of the container to occupy the maximum space the parent widget has to offer by using maxWidth and maxHeight properties respectively.
Get Bhavesh Mankar’s stories in your inbox
Join Medium for free to get updates from this writer.
Now lets paint the same container but this time we will use minWidth and minHeight for specifying values for width and height respectively.
// Modify the LayoutBuilderExample as follows
LayoutBuilder(
builder: (context, constraints) {
//Some Print Statements
debugPrint(
"Max Width: ${constraints.maxWidth}\t\t\tMax Height: ${constraints.maxHeight}");
debugPrint(
"Min Width: ${constraints.minWidth}\t\t\tMin Height: ${constraints.minHeight}");
return Container(
width: constraints.minWidth, //use minWidth instead of maxWidth
height: constraints.minHeight, //use minHeight instead of maxHeight
color: Colors.purple,
);
},
);
Aaand…. nothing changed. We still get the exact same output. Does that mean both min and max values are the exact same? To verify that we have have already included some print statements in our code. On checking the console of the IDE, we find that the output of print statements show exact same value for min and max values of width and height respectively. But why are the min and max constraints the exact same? Is there some problem?
Well, there is no problem, its just how flutter renders the widgets. It is essential to understand how constraints work and how it affets the rendering of a widget. Fortunately, all of that is clearly explained in an article by Marcelo Glasberg, which can also be found in flutter docs here.
I recommend the readers to check article by Marcelo Glasberg before reading further, as it is important to understand constraints to fully grasp the concepts discussed in this article.
Creating Responsive layouts using Layout Builder
Now, lets move towards the most intresting section of the article which is creating responsive layouts using LayoutBuilder. We will also look at some exceptions that developers might face while creating responsive layouts while using LayoutBuilder.
So, LayoutBuilder informs us about constraints, and we can leverage this information to create responsive layouts. But how exactly?
Well, by now we know that constraints is a set of four doubles minWidth, minHeight, maxWidth and maxHeight incoming from parent widget. If you look at the code with which we created purple container, we have used the values of maxWidth and maxHeight for defining containers dimension. In our case parent was the screen so the incoming constraints were the acthual height and the width of the screen due to which the container filled the entire screen. Also the reasons due to which we observed the same results when using minWidth and minHeight were because, of the incoming constraints being tight(min and max constraints are the same).
OK. Now, let’s look at another example where we can use layout builder to do the same thing we used MediaQuery for. In the first article of the series we looked at a small code snippet(here) which used MediaQuery to get the device dimensions and return either desktop, tablet or mobile layout.
We can achive the same functionality using LayoutBuilder. Here’s how,
//I recommend readers to read the comments for better understanding of code
import 'package:flutter/material.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: LoadLayout());
}
}
class LoadLayout extends StatelessWidget {
const LoadLayout({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder( //Used Layout Builder
builder: (context, constraints) {
if (constraints.maxWidth >= 1024) {
return buildDesktopLayout(parentConstraints: constraints);
} else if (constraints.maxWidth >= 600) {
return buildTabletLayout(parentConstraints: constraints);
} else {
return buildMobileLayout(parentConstraints: constraints);
}
},
//Alternatively switch-case can be used instead of if-else
// builder: (context, constraints) {
// return switch (constraints.maxWidth) {
// >= 1024 => buildDesktopLayout,
// >= 600 => buildTabletLayout,
// _ => buildMobileLayout,
// }(parentConstraints: constraints);
// },
);
}
}
//layout functions desktop
Widget buildDesktopLayout({required BoxConstraints parentConstraints}) {
return Scaffold(
appBar: AppBar(
title: Text(
//display acthual height of the screen
"Desktop(Acthual Height: ${parentConstraints.maxHeight})"),
),
body: buildBody(color: Colors.indigo),
);
}
//layout functions tablet
Widget buildTabletLayout({required BoxConstraints parentConstraints}) {
return Scaffold(
appBar: AppBar(
title: Text("Tablet(Acthual Height: ${parentConstraints.maxHeight})"),
),
body: buildBody(color: Colors.purple),
);
}
//layout functions mobile
Widget buildMobileLayout({required BoxConstraints parentConstraints}) {
return Scaffold(
appBar: AppBar(
title: Text("Mobile(Acthual Height: ${parentConstraints.maxHeight})"),
),
body: buildBody(color: Colors.teal),
);
}
//buildBody function
Widget buildBody({required Color color}) {
return LayoutBuilder(
builder: (context, constraints) {
return Container(
width: constraints.maxWidth, //width based on maxWidth constraint
height: constraints.maxHeight, ////width based on maxWidth constraint
color: color,
child: Center(
child: Text(
//display available height and width
"Width:${constraints.maxWidth} , Height:${constraints.maxHeight} ",
style: TextStyle(
color: Colors.white,
fontSize: constraints.maxWidth *
0.04, //fontSize based on incoming maxWidth constraint
fontWeight: FontWeight.bold,
),
),
),
);
},
);
}Here is the breakdown of the code,
In the code above we have a widget called as LoadLayout which returns a LayoutBuilder. Since LoadLayout is used as home, the parent is screen itself and screen dimensions are constraints. We have defined three functions called buildDesktopLayout(), buildTabletLayout(), buildMobileLayout() which are returned by the builder function based on the maxWidth constraint. Each of the layout function takes parentConstraint as a parameter and returns a Sacffold which has an appBar whose title is a Text widget which the name of the layout and the maxHeight constraint. For the body of the Scaffold we have used a function called as buildBody(). It takes a color parameter. This function also returns a LayoutBuilder. Its builder function returns a Container whose color property is set to ‘color’ which is passed during function call. Container’s child is a Centre whose child is a Text widget which displays the incoming constraints.
Select Windows(windows-x64) as the device in IDE and run the application.
Initially, it will return buildDesktopLayout() with indigo color and since our application is responsive we can resize the app and see the magic. As the width is reduced and certain breakpoint is reached different layouts are returned by LayoutBuilder of LoadLayout. Also since we have used maxWidth to set fontSize, the font size reduces as the window shrinks horizontally.
There is one thing all of us might have noticed and that is the height. Why is the height displayed differently on the appBar and on the body? That is because LayoutBuilder, as we all know by now, informs about constrains incoming from the parent. Since we have two LayoutBuilders used at different places with different parents we get different constraints.
The first LayoutBuilder is used by LoadLayout whose parent is the screen itself, so naturally, the constraints will be tight and will be equal to acthual dimensions of the screen. In our code we have passed these constraints to the parentConstraints parameter of layout functions and displayed the acthual height in the appBar.
The second LayoutBuilder is used by buildBody() function which is called by layout functions as the Scaffold’s body. So for buildBody() function‘s LayoutBuilder it is the Scaffold who is the parent. Since the appBar is already present, the available space will be less and vertical constraint i.e the height, will be reduced. This is the available height which is displayed at the Centre of the Container by the Text widget. Also, the fontSize of the text is adjusted based on maxWidth, so as the window is resized, the text responds to these changes and adjusts its size accordingly.
Isn’t that great. We can use LayoutBuilder anywere in the Widget tree make informed decisions about the layout based on the availabiliy of space. This allows us to create responsive layout which looks alomost identical across various devices and platforms. We can combime MediaQuery and LayoutBuilder to create responsive apps. But thats not all there other things like GridView, ListView, Responsive Text etc. that are needed to be taken into account for creating a trully responsive app. In further parts we will look at each of these and discuss about Creating Responsive Layouts.
Conclusion
Using LayoutBuilder in Flutter apps is a helps in adjusting layouts dynamically based on available space, making sure the app looks great on any device. The combination of LayoutBuilder with other widgets like MediaQuery allows making informed layout decisions, enhancing both usability and user experience. Whether designing for mobile, tablet, or desktop, LayoutBuilder provides information needed to build responsive apps.
I hope that this blog provided you with the information required about LayoutBuilder and helped in understanding how responsive layouts are created.
For reding the entire blog,







