Leave a rating/review
This video Make Your App Design Responsive was last updated on Nov 9 2021
Making your apps adapt to different screen sizes is essential in giving your users a good in-app experience. Flutter offers us various widgets to help with building responsive apps. But do we really need this for our app? Well let’s find out. I’ll change the device orientation to landscape mode. You can see that the ArticleCard
widgets doesn’t really look nice. They are quite big for this view.
While developing apps, you need to test on multiple devices and orientations to make sure that your app adapts to different screen sizes and modes. There’s no room for: “Oh! It works on my device and it should work on your device too.” That’s an absolute no no.
The first approach we would explore is the use of the MediaQuery
class. This class is already added by the MaterialApp
and it is updated whenever the screen dimensions change. A common example is when you rotate your device. The MediaQuery
class gives us access to stuffs like the screen size and device orientation and more. To access this information, you simply use the MediaQuery.of()
method.
Note that using this method would automatically rebuild your widget if you change the screen size by let’s say rotation your device. The starter project of this episode, includes a different card layout that would use a Row widget to display the article image side by side with the article detail. This would be displayed when our device is in landscape mode.
Currently, the default card passed is the TallCard
. It contains the same code previous code but this time around, we extracted it into a new widget called TallCard
. While the other widget that uses a Row
is the WideCard
. Make sure you go through the code to see how i created those widgets. And do check out the CardBanner
widget too i added a simple logic for the wide card to make it adapt well.
Now, let’s update our card widget to use this. Enter the following code inside the build
method of the ArticleCard
class:
...
@override
Widget build(BuildContext context) {
bool isPortrait =
MediaQuery.of(context).orientation == Orientation.portrait;
...
child:
isPortrait ? TallCard(article: article) : WideCard(article: article),
);
}
...
In here, we check to see if the device is in portrait mode. We get that information from the orientation
getter. Then if the device is in potrait mode, we return the tall card, else we return the wide card. I should update that to a wide card. Save your work. Let’s go ahead and try it out by changing the orientation. You can see the wide card is displayed in landscape mode.
Remember, whenever the screen size changes the MediaQuery.of()
method rebuilds the widget with the new apppearance. Notice that the spacing between the column items for the card detail doesnt look good in landscape mode. We want more spacing between them. We can also use MediaQuery.of()
method to solve this.
Scroll to the CardDetail
widget and update it like so:
...
@override
Widget build(BuildContext context) {
bool isPortrait =
MediaQuery.of(context).orientation == Orientation.portrait;
return Container(
height: isPortrait ? 130 : 150,
...
// Remove the SizedBox() in btw items in the Column
First, we changed the Padding
widget into a Container
. We do this because the Padding
widget doesnt have box related properties. Then we give the Container
a height of 130 if the device is in portrait mode else a height of 150
. After that, we removed the SizedBox
because we have given the enclosing container a height which means the MainAxisAlignment
of the Column can now add spaces between its children. In other words, you dont need a SizedBox
to add spacing if the enclosing container has a fixed height and the Column
has a mainAxisAlignment
.
Save your work. And you can see the children of the column are more spaced out.
The MediaQuery
class works best when you want to adapt widgets based on device orientation. But using MediaQuery.of()
method always calls the build method when the screen size changes. This is not really a bad thing but we have another widget that could help with this.
Flutter provides the LayoutBuilder
widget that helps adapt widgets based on their parents size. The building of the widget is postponed until layout because it needs its parent’s size information. Let’s change our code to use a LayoutBuilder
. Update your to the following:
// Card
...
child: LayoutBuilder(
builder: (context, constraints) {
bool isTablet = constraints.maxWidth > 600;
return isTablet
? WideCard(article: article)
: TallCard(article: article);
},
),
...
The LayoutBuilder
has a builder
callback function that is called at layout time and provides its parent widget’s box constraints. A BoxConstraint
is simply the size information of a box. Next, it checks if the maxWidth
of the parent box is greater than 600
which shows that the device could be a tablet. If it is a tablet, we return the wide card widget, else we return the tall card. Opps this should be the wide card and the second one should be the tall card.
That’s what you get when you copy and paste code unneccesirly.
Note that this width is that of the parent widget and not the screen width. But remember that the card is inside a ListView
and the ListView
takes up the entire screen’s width so you’ll get the same effect. Save your work.
Seems theres an error and i can see that red sign on the scroll bar. Let’s scroll to that postion. And you can see, the linter complains that the code we commented out is greater than 80 lines. I’ll fix that. Save my work. And try it out.
And there you have it, the layout updates accordingly when the parent widget’s width is greater than 600
.