DEV Community

Cover image for Build a Responsive Flutter App with The Power of MediaQuery
Aditya Rohman
Aditya Rohman

Posted on

Build a Responsive Flutter App with The Power of MediaQuery

First of all, I wanna say that I’m so excited about the new release of Flutter 2.2 with null-safety stable. But we'll talk about it later.


When we develop an app with a multi-platform framework like Flutter. Responsive and Adaptive are something important to care about. Because our app isn’t only run on a smartphone, but in various devices with different aspect ratio and visual density. So, as a developer, our task is to make sure our app layout is stable, good, and adaptive to all of those devices.
Okay, in this article I will show you step-by-step how to build a responsive app with Flutter. Feel free to follow along.


Load The Project

I have created a starter project. So, you don’t have to create a project from scratch. Just clone this repository

GitHub logo codestronaut / flutter_responsive_onboarding

Demo flutter responsive handle with MediaQuery

Note:
On that repository, select the branch flutter_responsive_onboarding_starter. Now, you’re ready to start

On that source code, I make separate packages/folders to keep our code more clean and maintainable. This is what the lib folder looks like:

Alt Text

Inside the lib folder there are several folders:

  • Screens: used to put all app screens or layouts
  • Utils: used to put all utilities such (i.e. constants, helper, etc)
  • Widgets: used to put widgets

Remember:
When developing an app, make sure to create single-responsibility functions, classes, and widgets (on Flutter). That's why it's better to separate the code into small pieces.

Working On The Widgets

Let’s begin with the widgets folder. We will write some code to construct the layout:

  • Title Widget
import 'package:flutter/material.dart';
import 'package:flutter_responsive_onboarding/utils/utils.dart';

class TitleWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    double deviceWidth = MediaQuery.of(context).size.width;

    return Center(
      child: Image(
        image: AssetImage('assets/title.png'),
        width: Utils.getDeviceType() == 'phone'
            ? deviceWidth / 2
            : deviceWidth / 3,
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Illustration Widget
import 'package:flutter/material.dart';
import 'package:flutter_responsive_onboarding/utils/utils.dart';

class IllustrationWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    double deviceWidth = MediaQuery.of(context).size.width;

    return Center(
      child: Image(
        image: AssetImage(
          'assets/illustration.png',
        ),
        width:
            Utils.getDeviceType() == 'phone' ? deviceWidth : deviceWidth / 1.2,
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Caption Widget
import 'package:flutter/material.dart';
import 'package:flutter_responsive_onboarding/utils/utils.dart';
import 'package:google_fonts/google_fonts.dart';

class CaptionWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: [
          Text(
            'Play Anywhere',
            style: GoogleFonts.inter(
              fontSize: Utils.getDeviceType() == 'phone' ? 28.0 : 46.0,
              fontWeight: FontWeight.w800,
            ),
          ),
          SizedBox(
            height: 16.0,
          ),
          Text(
            'The video call feature can be\naccessed from anywhere in your\nhouse to help you.',
            textAlign: TextAlign.center,
            style: GoogleFonts.inter(
              fontSize: Utils.getDeviceType() == 'phone' ? 18.0 : 28.0,
              fontWeight: FontWeight.w300,
              color: Color(0xFFA6A6A6),
            ),
          ),
        ],
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Indicator Widget
import 'package:flutter/material.dart';
import 'package:flutter_responsive_onboarding/utils/utils.dart';

class IndicatorWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            Icons.paid,
            color: Color(0xFF4756DF),
            size: Utils.getDeviceType() == 'phone' ? 24.0 : 32.0,
          ),
          Icon(
            Icons.paid,
            color: Color(0xFFB9BFF3),
            size: Utils.getDeviceType() == 'phone' ? 24.0 : 32.0,
          ),
          Icon(
            Icons.paid,
            color: Color(0xFFB9BFF3),
            size: Utils.getDeviceType() == 'phone' ? 24.0 : 32.0,
          ),
        ],
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Button Widget
import 'package:flutter/material.dart';
import 'package:flutter_responsive_onboarding/utils/utils.dart';
import 'package:google_fonts/google_fonts.dart';

class ButtonWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    double deviceWidth = MediaQuery.of(context).size.width;

    return Container(
      width: Utils.getDeviceType() == 'phone'
          ? deviceWidth / 2
          : deviceWidth / 2.5,
      height: Utils.getDeviceType() == 'phone' ? 56.0 : 72.0,
      child: ElevatedButton(
        child: Text(
          'Get Started',
          style: GoogleFonts.roboto(
            fontSize: Utils.getDeviceType() == 'phone' ? 18.0 : 24.0,
          ),
        ),
        style: ElevatedButton.styleFrom(
          primary: Color(0xFF4756DF),
          elevation: 0.0,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(
              Utils.getDeviceType() == 'phone' ? 15.0 : 20.0,
            ),
          ),
        ),
        onPressed: () {},
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

All right, all of our widgets are ready and have a single responsibility. If you notice, we use MediaQuery to get the device width. Then, we manipulate the width of all widgets that we have to make it more sense when the app running on smartphones and tablets.

double deviceWidth = MediaQuery.of(context).size.width;
Enter fullscreen mode Exit fullscreen mode

The variable deviceWidth will change as well as the device type change. Until this step, you may have errors in your widget's code. To control the UI behavior, add the following code to Utils Class:

import 'package:flutter/material.dart';

class Utils {
  static String getDeviceType() {
    final data = MediaQueryData.fromWindow(WidgetsBinding.instance!.window);
    return data.size.shortestSide < 550 ? 'phone' : 'tablet';
  }
}
Enter fullscreen mode Exit fullscreen mode

In Utils class, we use MediaQueryData to specify the kind of devices. We get the shortest side of the device. We can specify the shortest side < 550 is a smartphone. Otherwise, it must be a tablet.

Complete The Pages

Let’s complete our on_boarding_screens.dart! Put all widgets on the column. This is the arrangement of the widgets:

import 'package:flutter/material.dart';
import 'package:flutter_responsive_onboarding/widgets/button_widget.dart';
import 'package:flutter_responsive_onboarding/widgets/caption_widget.dart';
import 'package:flutter_responsive_onboarding/widgets/illustration_widget.dart';
import 'package:flutter_responsive_onboarding/widgets/indicator_widget.dart';
import 'package:flutter_responsive_onboarding/widgets/title_widget.dart';

class OnBoardingScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.symmetric(
            horizontal: 16.0,
            vertical: 24.0,
          ),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              // Here is where the widget will build
              TitleWidget(),
              Column(
                children: [
                  IllustrationWidget(),
                  CaptionWidget(),
                  SizedBox(
                    height: 16.0,
                  ),
                  IndicatorWidget(),
                ],
              ),

              ButtonWidget(),
            ],
          ),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, our app is ready. Let’s run it and see the result on both a smartphone and a tablet. Here are the results:

Alt Text

All right. So, that’s the step of how to make your Flutter app more responsive on any kind of device. For web and other OS, you can add more conditions in the Utils file.

This is the traditional method to make your app responsive. Flutter has provided tools for that. But, if you want to work faster, then you better use third-party packages like the following:


That’s all from me. If you like this kind of article, please give me claps :) also if you have any questions, feel free to post a comment to this article. Thank you so much!

For the complete source code, you can check it here:

GitHub logo codestronaut / flutter_responsive_onboarding

Demo flutter responsive handle with MediaQuery

Discussion (0)