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
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:
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,
),
);
}
}
- 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,
),
);
}
}
- 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),
),
),
],
),
);
}
}
- 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,
),
],
),
);
}
}
- 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: () {},
),
);
}
}
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;
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';
}
}
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(),
],
),
),
),
);
}
}
Now, our app is ready. Let’s run it and see the result on both a smartphone and a tablet. Here are the results:
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:
- Sizer (Link to Pub.dev)
- Responsive Framework (Link to Pub.dev)
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:
Top comments (0)