visit
However, my perspective changed during my third project when my boss tasked me with streamlining the process of goods entering and leaving the warehouse, making it paperless. Until then, workers had to print documents before receiving or picking up goods. To achieve this, we decided to develop a Warehouse App to be used on Tablet devices, which required me to learn how to create widgets that could cater to larger screens within the Flutter app.
Here are 3 methods, I picked along the way, that you can employ when making responsive layouts in Flutter apps.
Let's have a look at the code below:
import 'package:flutter/material.dart';
class ResponsivePadding extends StatelessWidget {
final Widget child;
const ResponsivePadding({
Key? key,
required this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: MediaQuery.of(context).size.width > 650
? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width / 3.8)
: const EdgeInsets.all(0),
child: child,
);
}
}
All you need to do is wrap your screen with the ResponsivePadding widget:
@override
Widget build(BuildContext context) {
return ResponsivePadding(
child: Scaffold()
....
Here’s the result:
Unlike Dynamic Padding, this approach captures all screen sizes and sets their layout accordingly. We have three layouts: Mobile, Tablet, and Desktop, and use a distinct widget for each one. I learned this technique from TheFlutter Way on YouTube - you can access the video through the reference link.
See the responsive code below:
import 'package:flutter/material.dart';
class Responsive extends StatelessWidget {
final Widget mobile;
final Widget tablet;
final Widget desktop;
const Responsive({
Key? key,
required this.desktop,
required this.mobile,
required this.tablet,
}) : super(key: key);
/// mobile < 650
static bool isMobile(BuildContext context) =>
MediaQuery.of(context).size.width < 650;
/// tablet >= 650
static bool isTablet(BuildContext context) =>
MediaQuery.of(context).size.width >= 650;
///desktop >= 1100
static bool isDesktop(BuildContext context) =>
MediaQuery.of(context).size.width >= 1100;
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
if (constraints.maxWidth >= 1100) {
return desktop;
} else if (constraints.maxWidth >= 650) {
return tablet;
} else {
return mobile;
}
});
}
}
From the code above, we have a StatelessWidget and return a LayoutBuilder. As you can see, we have 3 constraints width. You can modify the value as you need.
Example implementation:
@override
Widget build(BuildContext context) {
return Responsive(
mobile: mobileWidget,
tablet: tabletWidget,
desktop: desktopWidget,
);
}
A generic function is a function that is declared with type parameters. When called, actual types are used instead of the type parameters.[
Extension Methods, introduced in Dart 2.7, are a way to add functionality to existing libraries.[
Btw, I found this very interesting idea on Twitter.
In Flutter we often use context. BuildContext takes care of widget location in the widget tree. So…. we will have context in all widget trees. Without context, the widget will not render.
Look at the code below:
extension Responsive on BuildContext {
T responsive<T>(
T defaultVal, {
T? sm,
T? md,
T? lg,
T? xl,
}) {
final wd = MediaQuery.of(this).size.width;
return wd >= 1280
? (xl ?? lg ?? md ?? sm ?? defaultVal)
: wd >= 1024
? (lg ?? md ?? sm ?? defaultVal)
: wd >= 768
? (md ?? sm ?? defaultVal)
: wd >= 640
? (sm ?? defaultVal)
: defaultVal;
}
}
With this method, we can achieve the same result as LayoutBuilder but with simpler code.
@override
Widget build(BuildContext context) {
return Container(
child: context.responsive<Widget>(
mobileWidget, // default
md: tabletWidget, // medium
lg: desktopWidget, // large
),
);
}
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: context.responsive<int>(
2, // default
sm: 2, // small
md: 3, // medium
lg: 4, // large
xl: 5, // extra large screen
),
.......
Result:
Original story published on