Custom Scaffold and vary the Drawer or side menu according to the screen size in Flutter
We will talk about how you can vary the Drawer so that it remains static if the screen is large and the advantages of creating a custom Scaffold.
We will talk about how you can vary the Drawer so that it remains static if the screen is large and the advantages of creating a custom Scaffold.
Static Menu Drawer, OrientationBuilder and more
I want to talk to you about two things mainly:
Since you notice that if we shrink the screen here it goes to the classic approach, this would be the mobile approach, that is, with the size of a mobile device screen. And if we grow it would be, for example, a web application or directly a tablet or directly an application for Mac, Linux or Windows. We have this other approach, since we have more space here, then we can take advantage of it to place more things. This is what we are going to see in this video. I am going to show you how I did it for the Academy application.
So here we remember that the top widget is the one for the material app, from there what we want is to create a page and how do we create a page we have here a helper which is the scaffold therefore each one of these options and each one of these options that your application is going to have, which in this case are the ones listed here, is going to have its own scaffold in which we always want to customize it, in this case to create this side menu as I showed you before with the functionality that it had before, obviously one way is to copy and paste every time the behavior that we are implementing here and with this the blessed Drawer you would have to copy it to each one of these pages, the problem with this I think is quite obvious that when you decide to change something in Scaffold, which in the end is like a global configuration but you can't manage it that way since you touch each one of those pages and that is why I decided to create a custom scaffold here, a custom scaffold which is the name that I gave it again which is the one that we use in the end on each page:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:desarrollolibre/generated/locale_keys.g.dart';
import 'package:desarrollolibre/components/drawer_component.dart';
import 'package:desarrollolibre/provider/app_model.dart';
import 'package:desarrollolibre/utils/windows_sizes.dart';
import 'package:desarrollolibre/utils/academy_helper.dart';
import 'package:desarrollolibre/utils/helpers.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
class CustomScaffold extends StatefulWidget {
final String title;
final Widget body;
final Widget? endDrawer;
final FloatingActionButton? floatingActionButton;
const CustomScaffold(this.title, this.body,
[this.endDrawer, this.floatingActionButton]);
@override
State<CustomScaffold> createState() => _CustomScaffoldState();
}
class _CustomScaffoldState extends State<CustomScaffold> {
late AppModel appModel;
bool thereIsInternet = true;
@override
void initState() {
appModel = Provider.of<AppModel>(context, listen: false);
if (!appModel.userCredentialsCheck) {
checkuserVerified(context);
}
init();
super.initState();
}
Future<bool> checkisInternetConnection() async {
var connectivityResult = await (Connectivity().checkConnectivity());
return ConnectivityResult.none != connectivityResult;
}
init() async {
thereIsInternet = await checkisInternetConnection();
if (!thereIsInternet) {
// no hay internet
setState(() {});
}
}
@override
Widget build(BuildContext context) {
if (!thereIsInternet) {
// no hay conexion
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Text('No hay Internet'),
);
}
return OrientationBuilder(
builder: (BuildContext context, Orientation orientation) {
if (getBreakpoint(MediaQuery.of(context).size.width) ==
WindowsBreakpoint.sm ||
getBreakpoint(MediaQuery.of(context).size.width) ==
WindowsBreakpoint.md) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: DrawerComponent(true),
endDrawer: widget.endDrawer,
floatingActionButton:
appModel.userIsLogging && !appModel.userVerified
? FloatingActionButton.extended(
onPressed: () {
AcademyHelper.verifyUserPost(appModel.userToken);
showToastMessage(context,
"${LocaleKeys.sentEmailTo.tr()} ${appModel.userEmail} ${LocaleKeys.verifyAccount.tr()}");
},
label: Text(LocaleKeys.verifyUser.tr()),
)
: widget.floatingActionButton,
body: SafeArea(child: widget.body));
} else {
//window lg
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
floatingActionButton: appModel.userIsLogging && !appModel.userVerified
? FloatingActionButton.extended(
onPressed: () {
AcademyHelper.verifyUserPost(appModel.userToken);
showToastMessage(context,
"${LocaleKeys.sentEmailTo.tr()} ${appModel.userEmail} ${LocaleKeys.verifyAccount.tr()}");
},
label: Text(LocaleKeys.verifyUser.tr()),
)
: widget.floatingActionButton,
body: Row(
children: <Widget>[
SizedBox(
width: 300,
height: double.infinity,
child: DrawerComponent(),
),
Container(
width: 1,
height: double.infinity,
color: Colors.grey,
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SafeArea(child: widget.body),
))
],
),
endDrawer: widget.endDrawer,
);
}
});
}
}
So that is the first trick here. Note that in this file called custom scaffold what I do is create a custom scaffold, basically it is a StatefulWidget in which I receive the elements that we always receive in the scaffold, which is the title and also the body, in this case this is also for something else, the Drawer end, that is, the Drawer that in my case would appear from here, which I use for example when I am here consuming a course.
The Drawer in Flutter is the one I have defined here:
return Drawer(
child: SafeArea(
child: Column(
children: <Widget>[
const SizedBox(
height: 15,
),
MediaQuery.of(context).size.height > 700
? SizedBox(
width: double.infinity,
height: 120,
child: CircleAvatar(
backgroundColor: Colors.purple,
child: Text(
appModel.userIsLogging
? appModel.userEmail.substring(0, 1).toUpperCase() +
appModel.userEmail.substring(1, 2)
: 'DL',
style:
const TextStyle(fontSize: 40, color: Colors.white),
),
),
)
: const SizedBox(),
const SizedBox(
height: 15,
),
Text('${LocaleKeys.hello.tr()} ${appModel.userEmail}'),
Expanded(child: _Items(activatePopInBack)),
ListTile(
leading: const Icon(Icons.circle, color: Colors.purple),
title: const Text('Dark Mode'),
trailing: Switch.adaptive(
value: userPreference.themeDarkMode,
activeColor: Colors.purple,
onChanged: (value) {
userPreference.themeDarkMode = value;
appTheme.darkTheme = value;
AcademyHelper.userExtraPost(appModel.userToken, value);
}),
),
],
),
),
);
}
}
Here I have the SafeArea and everything else, here I have the Drawer, what I would need now is to simply use this customized Scaffold, but it would be something quite obvious and here is where you have a bit of the magic of what was discussed, now with this customized scaffold we could use it on any page, we already had the basic structure that we are going to replicate on each of the pages, which is what we want to achieve.
So how to use it there is nothing strange basically like any other widget here we have the name custom scuffle I am going to open here some of the pages that I have here in page This is my scheme for example this is the list if I search here I use it again instead of using scaffold here directly I create my custom scaffold here there are several ways to do it I could also inherit from the class extend it what do I know but this was the solution that I chose I like this way much more in base widget and I simply pass the parameters in this case is if the content is loaded not the title would be this exactly this this It would be for the body since I placed them well that I receive it by default in I did not place them positional and basically that is it so for me it was very easy for example when I decided to also implement the part of the button that I placed it here as optional I simply touched this file and modified to place my floating button that I sometimes use for example here for the post to place the filtering here in base and also on other pages like courses and so on So with this we learned the first part that was the first objective of this video was to show you how you can create a custom scaffold and use it on each of your pages since again this is a page if I open another page you will see that it follows the same tune I will always use my custom scaffold with the structure that I already defined that I can simply inherit and in case I want to change something I can easily do it from here from a single file and not touch the n pages that we have here so how did I make it work this way and leave the fixed menu here when the screen is large and that shows a drop-down menu, a drawer when the screen is small, very easy in the end let's remember that in flurer everything is a widget, this is a widget therefore we can place it or we can work with it as we want here if we review:
class IndexPage extends StatefulWidget {
static const String ROUTE = "/academy/book";
String title;
// cursos que compro el usuario
bool listMyBooks;
// solo cursos gratis
IndexPage([this.title = '', this.listMyBooks = false]);
@override
_IndexPageState createState() => _IndexPageState();
}
class _IndexPageState extends State<IndexPage> {
BookResponseModel bookResponseModel = BookResponseModel.empty();
List<CategoryModel> categories = [];
late AppModel appModel;
// filters
String _onlyLang = '';
String _freePay = '';
bool _buyed = false;
int _categoryId = 0;
@override
void initState() {
appModel = Provider.of<AppModel>(context, listen: false);
_dataList();
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
// verifica si el usuario tiene cursos en su cuenta
if (widget.listMyBooks && appModel.userMyBooks.isEmpty) {
return Center(
child: Text(
LocaleKeys.noBooksRegisteredInYourAccount.tr(),
style: Theme.of(context).textTheme.displayMedium,
));
}
if (bookResponseModel.books.isEmpty) {
return const Center(child: CircularProgressIndicator());
}
return CustomScaffold(
widget.title,
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 700),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: bookResponseModel.books.length,
itemBuilder: (_, int position) {
return _itemBook(position, context);
}),
),
),
),
null,
FloatingActionButton(
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return _bottomSheetFilter();
},
);
},
child: const Icon(Icons.filter_alt_rounded),
),
);
}
I am using here what is the orientation builder API that will be executed every time we change the screen and here we can place This I copied a little from telwin or bstr of how ccs works around here I define is what I chose that a small screen in width has to be 576 pixels and the long one would be from 992 Well here I used up to the medium since I did nothing with this one the large one would be from 720 and from there I decide to do something that is my something good let's see it here Note that based on this conditional:
OrientationBuilder(
builder: (BuildContext context, Orientation orientation) {
if (getBreakpoint(MediaQuery.of(context).size.width) ==
WindowsBreakpoint.sm ||
getBreakpoint(MediaQuery.of(context).size.width) ==
WindowsBreakpoint.md) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: DrawerComponent(true),
***
} else {
//window lg
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
***
body: Row(
children: <Widget>[
SizedBox(
width: 300,
height: double.infinity,
child: DrawerComponent(),
),
Container(
width: 1,
height: double.infinity,
color: Colors.grey,
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SafeArea(child: widget.body),
))
],
What we have up here, well, precisely this, if the screen is small, which for me are the two dimensions that I just specified, then I'm going to use the drag option that Scaffold already provides us with and here I place my Drawer, which is what I was telling you before. This is ultimately a widget. This is what we return here with its areas, its buttons, its circle to place the avatar here and so on. But here comes a bit like who says the magic or the trick of all this. If these dimensions are not met, it means that the screen is large, and if not, it is small and is displayed with the Drawer option. Note that the second option is the widget itself, here it would be for the title and here it would be for the page itself and it is that simple. With this we have this behavior since again that listener that we have here is going to be executed every time we resize the screen, whether on mobile devices that simply change the orientation from portray to landscape, which would be something like this. If we have it like this, it changes this orientation or directly on PC or Mac or Linux that we resize the screen. Another important thing, well, that was what I was also telling you at the beginning, but it is important to highlight it, is that with this we can, with the custom scaffold, we can now, when necessary, add an additional process. In this case, I am adding the one to check the internet connection through a package that I will talk about in another class. So now this is like making only one change automatically because let us remember that I am using this file on all the pages, and that change or this update or this verification would be on each one of the pages again. Note that this is the nice thing about this, here in the init State I am going to place a print here, I place anything here, I am going to save every time we change pages this message will be executed and with this every time we enter a page we can do the verification that we want here I don't know what happened I think it's an issue with the image but the message did appear up there so we can also take advantage of it and these are the advantages we have of having a custom scaffold in Flutter.
- Andrés Cruz
Develop with Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter
I agree to receive announcements of interest about this Blog.
!Courses from!
10$
On Udemy
There are 1d 10:54!
!Courses from!
4$
In Academy
View courses!Books from!
1$
View books