Flutter responsive design: Size doesn’t (always) matter
Designing an application may be a real struggle when you have to made it to smartphones, tablets and web. Commonly, you may define general layout by retrieving the device screen size through the MediaQueryData instance.
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
if (screenWidth < 600.0) {
return _buildPhoneLayout();
}
else if (screenWidth < 1000.0) {
return _buildTabletLayout();
}
else {
return _buildDesktopLayout();
}
}
This example above a simple but pragmatic way to lay out your application page. The only thing I don’t like so much is that you have to define limits for device detection (devices under 600.0 logical pixels width are considered as phones in this example). Not so straightforward, as I’m not able to find a concrete answer in Flutter documentation…
Using this method for every widgets you create can be a waste of time, source of errors in UI and of course pain to develop. Do you really want to scatter responsive design logic into your widgets? How do you ensure code homogeneity ?
A good approach is to make widgets responsive to its parent size and constraints, in a transparent way (I mean without developing any specific code handling parent allowed size, screen size, et cetera…).
The Flutter SDK has an answer for you, in the form of a widget: the OverflowBar !
This widget will display its children horizontally, as Row widget does, if it has enough space. If not, it display children vertically, as a Column widget. Added to that, some other input parameters allows you to set a spacing between every child.
Demonstration
Let’s take an example to illustrate OveflowBar usage. Here is the design we choose:
Any familiarity with that layout pattern ? We often see it in “single page websites”. Each element display themselves in a row, when they have enough space. Element “direction” are switched from one to another, only in desktop version. On contrary, the mobile version display elements vertically, always displaying the image first.
In Flutter, this layout behavior is very easy to do.
Widget design
First, let’s create our own widget, containing all needed properties to display such an element.
class BlogOverview extends StatelessWidget {
final Widget image;
final String title;
final String text;
final bool reversed;
BlogOverview({this.title, this.text, this.image, this.reversed = false});
Then, in the build method, we will use the OverflowBar to layout our element.
@override
Widget build(BuildContext context) {
return OverflowBar(
overflowAlignment: OverflowBarAlignment.center,
spacing: 20.0,
overflowSpacing: 16.0,
textDirection: reversed ? TextDirection.rtl : TextDirection.ltr,
children: [
_buildImageWidget(),
_buildDescriptionPanel(),
],
);
}
In this example, the spacing and overflowSpacing refer both to the space between our image and our description. Spacing will be used when displayed as a row, whereas overflowSpacing is used when the children are displayed as a column.
Switching children position is done by the textDirection input parameter.
Widget integration in a listview
Our created widget will adapt itself with the available space. So designing an entire screen is as usual.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter OverflowBar Demo'),
),
body: ListView.separated(
itemCount: 20,
itemBuilder: (context, index) => _buildBlog(index),
separatorBuilder: (context, index) => Divider(height: 80.0,),
padding: EdgeInsets.symmetric(horizontal: 12.0),
),
);
}
Widget _buildBlog(int index) {
return Container(
alignment: Alignment.center,
padding: EdgeInsets.all(10.0),
child: BlogOverview(
title: 'Blog Nature #${index+1}',
text: ...,
image: ...,
reversed: index.isOdd,
),
);
}
The most important thing in that code is that there is no need to know what is the screen size!
Conclusion
In my opinion, the best responsive applications don’t overuse screen size.
In term of code quality, you implemented the design element (image + description) as a single widget, very succinct, and easy to understand (no complex layout algorithm).
In terms of application maintenance, there is no weird computation determining if we have to layout elements horizontally or vertically. The OverflowBar do it itself and, most importantly, when the size is known (in its render object).
In terms of design, you can ensure the layout direction by defining to widgets a size. In this example, images size are 300x300 logical pixels, and description width is constrained to 300. Therefore, we are able to ensure that for device width lower than 600 (we consider them as smartphones), the widget will layout vertically.
As always, you will be able to find the code developed for that article here:
Follow Flutter Community on Twitter: https://www.twitter.com/FlutterComm