This tutorial shows you how to use LayoutBuilder
widget in Flutter, which can be useful for creating responsive designs.
When building a widget, sometimes you may need to determine the size based on the size of its parent. For example, there is an icon with a specific size. If the available space is below a threshold (e.g. because of the display size, orientation, etc.), you want to render a completely different icon. To do so, you have to know the size constraints from the parent. That's where the LayoutBuilder
can be a useful thing.
Using LayoutBuilder
Below is the constructor of LayoutBuilder
.
const LayoutBuilder({
Key? key,
required Function(BuildContext context, ConstraintType constraints) builder,
});
Besides the key
argument, which is also present in other Flutter widgets, it only has the builder
argument. It's a required argument, which means you have to pass it. The argument requires a function that returns a Widget
and has two positional arguments. The first one is BuildContext
, which is the BuildContext
of the LayoutBuilder
. The other argument is BoxConstraints
, which is the constraints from the parent widget.
There are some conditions that the builder
function will be called:
- When the widget is going to be laid out for the first time.
- When the layout constraints from the parent widget changes.
- When the widget is updated by the parent.
- When the dependencies subscribed by the
builder
function change.
The BoxConstraints
itself has some properties that can be useful to determine how to build the child widget.
double maxHeight
: The maximum height that satisfies the constraints.double maxWidth
: The maximum width that satisfies the constraints.double minHeight
: The minimum height that satisfies the constraints.double minWidth
: The minimum width that satisfies the constraints.Size biggest
: The biggest size that satisfies the constraints.Size smallest
: The smallest size that satisfies the constraints.bool hasBoundedHeight
: Whether there is an upper bound on the maximum height.bool hasBoundedWidth
: Whether there is an upper bound on the maximum width.bool hasInfiniteHeight
: Whether the height constraint is infinite.bool hasInfiniteWidth
: Whether the width constraint is infinite.bool hasTightHeight
: Whether there is exactly one height value that satisfies the constraints.bool hasTightWidth
: Whether there is exactly one width value that satisfies the constraints.bool isTight
: Whether there is exactly one size value that satisfies the constraints.bool isNormalized
: Whether the object's constraints are normalized (if the minimums are less than or equal to the corresponding maximums).BoxConstraints flipped
: A box constraints with the width and height constraints flipped.
In the example below, we are going to use the constraints from BoxConstraints
to dynamically set the size of a widget to have a width of 80% of the parent's width and a height of 20% of the parent's height. In addition, there is also an Icon
whose size is determined by the size constraints. If the constraints are larger than the threshold, it will render an icon with a larger size. Having the size constraints of the parent widget, you can also do other things such as rendering a completely different widget or hiding a widget.
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
color: Colors.teal,
width: constraints.maxWidth * 0.8,
height: constraints.maxHeight * 0.2,
child: const Center(
child: Text('Woolha.com', style: TextStyle(color: Colors.white)),
),
),
Icon(
Icons.flutter_dash,
size: constraints.maxWidth >= 300 && constraints.maxHeight > 300 ? 50 : 30
),
],
);
}
)
Full Code
Below is the full example where the size constraints from the parent widget can be changed.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Woolha.com Flutter Tutorial',
theme: ThemeData.light().copyWith(
badgeTheme: const BadgeThemeData(
backgroundColor: Colors.teal,
)
),
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
@override
State<StatefulWidget> createState() {
return HomeState();
}
}
class HomeState extends State<Home> {
String size = 'small';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Woolha.com Flutter Tutorial'),
backgroundColor: Colors.teal,
),
body: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
Container(
color: Colors.yellow,
width: size == 'small' ? 200 : 300,
height: size == 'small' ? 300 : 400,
child: const MyWidget(),
),
ListTile(
title: const Text('Small'),
leading: Radio<String>(
value: 'small',
groupValue: size,
onChanged: (String? value) {
setState(() {
size = value ?? 'small';
});
},
),
),
ListTile(
title: const Text('Big'),
leading: Radio<String>(
value: 'big',
groupValue: size,
onChanged: (String? value) {
setState(() {
size = value ?? 'big';
});
},
),
),
],
),
),
);
}
}
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
color: Colors.teal,
width: constraints.maxWidth * 0.8,
height: constraints.maxHeight * 0.2,
child: const Center(
child: Text('Woolha.com', style: TextStyle(color: Colors.white)),
),
),
Icon(
Icons.flutter_dash,
size: constraints.maxWidth >= 300 && constraints.maxHeight > 300 ? 50 : 30
),
],
);
}
);
}
}
Output:
Summary
If you need to build a widget based on the layout constraints from the parent, you can use the LayoutBuilder
widget. It allows you to get the parent's constraints and write your own logic to build the child widget.
You can also read about:
- How to use
Builder
widget in Flutter, if you need to get theBuildContext
but do not need the constraints from the parent.