Responsive Design for Flutter: Getting Started
In this Flutter Responsive Design tutorial you’ll learn how to build a Flutter app that responds to layout changes such as screen size and orientation. By JB Lorenzo.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Responsive Design for Flutter: Getting Started
15 mins
- What is Responsive Design?
- Handling Different Device Types and Screen Sizes
- Handling Keyboard State Changes
- Handling Orientation Changes
- Getting Started
- Using MediaQuery to Decide the Layout
- Using Widgets in Responsive Design
- Using LayoutBuilder and OrientationBuilder
- Auto-Resizing Text Based on Parent Widget Size
- Using FittedBox
- Providing Constraints to Children
- Using AspectRatio
- CustomMultiChildLayout
Sometimes the configuration changes for an app on a mobile device. Maybe a keyboard suddenly appears, or the user rotates the device. Or perhaps you want to display your app on both small and large devices.
At any rate, your app needs to be responsive to these layout changes. If you use Responsive Design, it will.
In this Flutter tutorial, you’ll:
- Build a chat app in Flutter that responds to layout changes.
- Learn to use Flutter’s
MediaQuery
,LayoutBuilder
,OrientationBuilder
,FittedBox
andAspectRatio
widgets. - Learn to handle orientation changes.
- Perform text resizing.
- Constrain a child widget in a column.
- Learn about the idea of the
CustomMultiChildLayout
widget.
What is Responsive Design?
The concept of Responsive Design is all about using one set of code that respond to various changes to layout. Platforms such as the iOS and Android native SDKs tackled this issue with “universal layouts.” The universal layouts respond to layout changes by using constraints and automatically resizing elements.
There are a variety of reasons why layout needs to responsively change from initial designs.
Handling Different Device Types and Screen Sizes
Your Flutter app can run on a phone, tablet, TV screen or (when they start supporting it) watch. Even within the category of phones there’s a large array of different resolutions and screen sizes. You need to make sure that the layout works as intended for each device type and screen size. Additionally, you can have different layouts for each device type and screen size.
With this in mind, Flutter provides several widgets and classes for responsive design. You’ll learn about some of these in this tutorial.
Handling Keyboard State Changes
Your interface might have text fields. The keyboard pops up when the user starts interacting with those fields. When that keyboard pops up, so do layout issues.
Android handles this with configuration changes for the keyboard. iOS uses internal notifications for keyboard state changes. But in Flutter, the Scaffold
class automatically handles keyboard state changes.
In detail, Scaffold
adjusts the bottom insets to make room for the keyboard. You can, however, disable this behavior by setting the resizeToAvoidBottomInset
property to false
.
You can read more about Scaffold
‘s interaction with the keyboard here.
Handling Orientation Changes
Let’s face it, users can rotate their device, and will do so frequently. You could disable responding to this within your app, locking your app into portrait or landscape mode, but your app wouldn’t be as fun and might in fact be less useful with respect to user experience.
When rotation happens in Flutter, MediaQuery
can help rebuild your layout. MaterialApp
and WidgetsApp
already use MediaQuery
. If you use them, Flutter rebuilds your widgets under MaterialApp
if orientation changes.
You can read more about MediaQuery
here.
Now that you understand the reasons for Responsive Design, it’s time to see what Flutter widgets can do to help.
Getting Started
Download the starter project by clicking on the Download Materials button at the top or bottom of the tutorial. Then, open the starter project in Android Studio 3.4 or later. You can also use VS Code, but you’ll have to adapt instructions below as needed.
You should be using a recent version of Flutter, 1.5 or above. Be sure to get Flutter dependencies for the project if prompted to do so by Android Studio with a ‘Packages get’ has not been run message.
You’ll find the starter project provides some parts of the chat app you’ll be working with.
Using MediaQuery to Decide the Layout
Now you should try using MediaQuery
to determine the layout. This is just one of the options you can use to respond to layout changes. You’ll get to use the other options in the next sections.
Go into the ChatListPage.dart file in the lib folder. Replace the contents of build(BuildContext context)
with:
// 1
var hasDetailPage =
MediaQuery.of(context).orientation == Orientation.landscape;
// 2
Widget child;
if (hasDetailPage) {
// 3
child = Row(
children: [
// 4
SizedBox(
width: 250,
height: double.infinity,
child: _buildList(context, hasDetailPage),
),
// 5
Expanded(child: _buildChat(context, selectedIndex)),
],
);
} else {
// 6
child = _buildList(context, hasDetailPage);
}
return Scaffold(
appBar: AppBar(
title: Text("Chats"),
),
body: SafeArea(
// 7
child: child,
),
);
With that, you layout the chat page using MediaQuery
. Here’s what you did:
- First, you check the orientation from
MediaQuery
. If it’s landscape, then you have a details page. - Second, you declare a child widget to use later.
- Next, if you have a details page, you declare the child as a row of widgets.
- For this, the row contains the list of chats as a first item.
- Then, the next item in the row is the chat page showing the conversation.
- If you don’t have a details page, the child will be the list of chats.
- Finally, you need to assign that child widget you created as a child of
SafeArea
.
Build and run the project — you should see a screen like this for portrait:
And this for landscape:
You’ll notice the layout is different for portrait and landscape. You can also try running it on a different device like a tablet.
Using Widgets in Responsive Design
As mentioned previously, there are other widgets that achieve the same effect as MediaQuery
. For example, LayoutBuilder
allows you to do the same thing. You’ll see that in this section.
Aside from that, there are other layout problems with the chat app. For instance, the text size on the user avatar does not scale. You will fix that in a bit.
Using LayoutBuilder and OrientationBuilder
LayoutBuilder
and OrientationBuilder
are alternatives to MediaQuery
for handling orientation changes. Time to see exactly how they work, starting with LayoutBuilder
.
First, open ChatListPage.dart, like you did in the previous section. Replace the contents of build(...)
with this:
return Scaffold(
appBar: AppBar(
title: Text("Chats"),
),
body: SafeArea(
// 1
child: LayoutBuilder(builder: (builder, constraints) {
// 2
var hasDetailPage = constraints.maxWidth > 600;
if (hasDetailPage) {
// 3
return Row(
children: [
// 4
SizedBox(
width: 250,
height: double.infinity,
child: _buildList(context, hasDetailPage),
),
// 5
Expanded(child: _buildChat(context, selectedIndex)),
],
);
} else {
// 6
return _buildList(context, hasDetailPage);
}
}),
),
);
With that, you layout the chat page again, this time using LayoutBuilder
. Here’s what you did:
- First, you declare a
LayoutBuilder
as the child ofSafeArea
. - Second, you determine if you have a details page using the maximum width of the parent widget. If it is greater than 600, then you have a details page.
- Next, if you have a details page, you declare child as a row of widgets.
- For this, the row contains the list of chats as a first item.
- Then, the next item in the row is the chat page showing the conversation.
- Finally, if you don’t have a details page, it’ll be the list of chats.
Build and run the project. You should see the same screen in the different orientations as in the previous section.
If you also want to try OrientationBuilder
, replace the lines with LayoutBuilder
and the setting of hasDetailPage
with this:
child: OrientationBuilder(builder: (builder, orientation) {
var hasDetailPage = orientation == Orientation.landscape;
This minor change has the same effect. Instead of reading the width of the parent widget, you read the parent widget’s orientation from the builder. If the orientation is landscape, then you have a details page.
Build and run the project. You should see the same screens as before. As you can see, each of these various widgets can solve the problem of different orientations and screen sizes.
Next, you’ll fix the text that’s not resizing in the user’s avatar.