Menu Drawer Estatico, OrientationBuilder y mas
Te quiero hablar de dos cosas principalmente:
Ya que fíjate que si encogemos aquí la pantalla pasa al enfoque clásico este sería el enfoque móvil es decir con la tamaño de una pantalla de un dispositivo móvil Y si crecemos ya sería por ejemplo una aplicación web o directamente una tableta o directamente una aplicación para Mac Linux o Windows que tenemos este otro este otro enfoque ya que tenemos aquí más espacio entonces podemos aprovecharlo para colocar más cositas es lo que nosotros vamos a ver en este video te voy a mostrar como yo lo hice para la aplicación de Academia
Entonces por aquí recordemos que el widget superior viene siendo el el del material app, a partir de ahí lo que nosotros queremos es crear una página y cómo hacemos para crear una página tenemos aquí un ayudante que viene siendo el scaffold por lo tanto cada una de estas opciones y cada una de de estas de las opciones que va a tener tu aplicación que en este caso son las listadas por acá va a tener su propio scaffold en el cual siempre lo queremos personalizar en este caso para crear este menú lateral como te mostraba antes con la funcionalidad que tenía antes obviamente una forma viene siendo copiar y pegar cada rato el comportamiento que estamos implementando por acá y con esto el bendito Drawer tuvieras que copiarlo en cada una de estas páginas el problema de esto creo que es bastante obvio que cuando decidas cambiar algo de Scaffold que a la final viene siendo como que una configuración global pero no lo puedes manejar de esa forma ya que tocar cada una de esas páginas y es por eso que yo decidí crear aquí un custom un custom scaffold que es el nombre que otra vez yo le di que es el que nosotros utilizamos a la final en cada página:
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,
);
}
});
}
}
Entonces ese viene siendo el primer truco por aquí Fíjate que en este archivo llamado custom scaffold lo que yo hago es crear un scaffold personalizado básicamente viene siendo un StatefulWidget en el cual yo recibo los elementos que siempre recibimos en el en el scaffold que viene siendo el título y tambien el body en este caso también esto es para otra cosa el en Drawer end es decir el Drawer que en mi caso aparecería desde acá que esto yo lo empleo por ejemplo para cuando estoy aquí consumiendo un curso.
El Drawer en Flutter es el que tengo aqui definido:
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);
}),
),
],
),
),
);
}
}
Aquí lo tengo el SafeArea y todo lo demás aquí tengo el Drawer lo que me faltaría ahora es simplemente utilizar este Scaffold personalizado pero sería algo bastante obvio y aquí es donde tienes un poco la maga de lo comentado ya con este scaffold personalizado que lo pudiéramos emplear en cualquier página ya tuviéramos la estructura básica que nosotros vamos a replicar en cada una de las páginas que es lo que nosotros queremos conseguir.
Entonces cómo utilizarlo no tiene nada de raro básicamente como cualquier otro widget aquí tenemos el nombre custom scuffle voy a abrir aquí alguna de las páginas que las tengo aquí en page Este es mi esquema por ejemplo este el listado si busco aquí lo empleo otra vez en vez de utilizar aquí scaffold directamente creo aquí mi custom scaffold hay varias formas de hacerlo también pudiera heredar de la clase extenderla qué sé yo pero esta fue la solución que yo elegí me gusta mucho más esta forma en base widget y simplemente le paso Los parámetros en este caso es para si el contenido está cargado no el título sería este justamente este este Sería para el body ya que los coloqué bueno que lo reciba por defecto en no los coloqué posicionales y básicamente es eso entonces para mí fue muy fácil por ejemplo cuando decidí también implementar la parte del botón que lo coloque aquí como opcional simplemente tocaba este archivo y modificaba para colocar mi botón flotante que a veces lo utilizo por ejemplo eemplo aquí para la de post para colocar aquí el filtrado en base y también en otras páginas como cursos y demás Entonces ya con esto aprendimos la primera parte que era el primer objetivo de este video que era mostrarte Cómo puedes crear un scaffold personalizado y utilizarlo en cada una de tus páginas ya que otra vez esta viene siendo una página si yo abro otra página vas a ver que sigue la misma sintonía siempre voy a utilizar mi custom scaffold con la estructura que ya yo definí que simplemente puedo heredar y en caso de que quiera cambiar algo lo puedo hacer fácilmente desde acá desde un solo archivo y no tocar Las n páginas que tenemos por aquí entonces cómo hice que funcionara de esta forma y dejar aquí el menú fijo cuando la pantalla es grande y que pasea un menú desplegable un drawer cuando la pantalla es pequeña muy fácil a la final recordemos que en flurer todo es un widget esto viene siendo widget por lo tanto lo podemos colocar o lo podemos trabajar como nosotros queramos por aquí si revisamos:
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),
),
);
}
Yo estoy empleando aquí lo que es el API de la orientación orientation builder que esto se va a ejecutar cada vez que nosotros cambiemos la pantalla y por aquí nosotros podemos colocar Esto me lo copié un poco de telwin o bstr de cómo funciona ccs por aquí yo defino es lo que yo escogí que una pantalla pequeña en el ancho tiene que ser de 576 píxeles y la larga sería a partir de 992 Bueno aquí utilicé hasta la media ya que no hice nada con esta la grande ya sería a partir de 720 y a partir de ahí decido hacer algo que es mi algo bueno vamos a verlo por aquí Fíjate que en base a este condicional:
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),
))
],
Que tenemos aquí arriba bueno precisamente esto si la pantalla es pequeña que para mí son las dos dimensiones que te acabo de especificar entonces voy a utilizar la opción de drare que ya nos provee el Scaffold y aquí coloco mi Drawer que era lo que te comentaba antes esto viene siendo la final un widget esto es lo que retornamos acá con sus se áreas sus botones su círculo para colocar aquí el avatar y demás pero y aquí viene un poco como quien dice la magia o el truco de todo esto si no se cumplen estas dimensiones significa que la pantalla es grande y si no es pequeña y se muestra con la opción de Drawer.
Fíjate que la segunda opción viene siendo el widget en sí aquí sería para el título y aquí sería para la página en sí y es así de simple ya con esto tenemos este comportamiento ya que otra vez ese listener que tenemos aquí se va a ejecutar cada vez que hacemos un resize de la pantalla ya sea en dispositivos móviles que simplemente cambie la orientación de portray a landscape que sería algo así si la tenemos as así cambia esta orientación o directamente en PC o Mac o Linux que nosotros redimension la pantalla otra importancia bueno que fue lo que también te comentaba al inicio pero es importante resaltarlo es que ya con esto nosotros podemos con el custom scaffold nosotros ya podemos cuando sea necesario agregar un proceso adicional en este caso yo estoy agregando el de revisar la conexión a internet mediante un paquete que ya hablaré en otra clase Entonces ya esto como a hacer solamente un cambio ya automáticamente porque recordemos que este archivo lo estoy empleando en todas las páginas ya ese cambio o esta actualización o esta verificación sería en cada una de las páginas otra vez fíjate que viene siendo lo bonito de esto aquí en el inic State voy a colocar aquí un print coloco aquí cualquier cosa voy a guardar cada vez que nosotros cambiemos de página se va a ejecutar este mensaje y con esto cada vez que nosotros entremos en alguna página podemos hacer la verificación que nosotros queramos aquí no sé qué pasó creo que es un tema con la imagen pero el mensaje sí apareció por ahí arriba entonces también lo podemos aprovechar para y estas son las ventaja que tenemos de tener un custom scaffold en Flutter.
- Andrés Cruz
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter
Acepto recibir anuncios de interes sobre este Blog.
!Cursos desde!
4$
En Academia
Ver los cursos!Libros desde!
1$
Ver los libros