Flutter UI Widgets

Nov 9 2021 Dart 2.14, Flutter 2.5, VS Code 1.61

Part 1: Flutter UI Widgets

12. Make Your App Design Responsive

Episode complete

Play next episode

Next
About this episode
See versions

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 11. Display Dialogs Next episode: 13. Explore Cupertino Widgets

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.