17
Flutter y el patrón Provider
Hola mundo!!
Tanto si desarrolla una aplicación nativa como una híbrida, el manejo del estado es un problema que debemos enfrentar y tener bajo control. Para administrar el estado global de una aplicación en Flutter
existen varias maneras, en este post abordaremos Provider
, una biblioteca bastante popular que nos ayudará con este trabajo.
Para ver el funcionamiento de Provider
, vamos a realizar una sencilla aplicación que nos ejemplifique.
Esta app muestra una pantalla con un AppBar
y su título, en el centro un Text
, y dos botones flotantes en la esquina inferior derecha, que tendrán como función cambiar el valor del texto que será mostrado tanto en el título del AppBar
como en el Text
del body de la app.
Tenemos en cuenta que el valor del texto será administrado de manera global.
Dejo el link del ejercicio para quien desee descargarlo:
https://github.com/hextiandro/flutter_provider_pattern
Primeramente instalaremos la biblioteca: provider: ^5.0.0
(En el momento que escribí el post esta era la versión).
La podemos encontrar en: https://pub.dev/packages/provider
Dentro de lib creamos una carpeta llamada widgets y dentro un archivo llamado floating_actions.dart, lib/widgets/floating_actions.dart
para crear el widget con los dos botones flotantes:
import 'package:flutter/material.dart';
class FloatingAction extends StatelessWidget {
Widget build(BuildContext context) {
(context);
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
child: Icon(Icons.wifi),
backgroundColor: Colors.orange,
onPressed: () {}),
SizedBox(
height: 10.0,
),
FloatingActionButton(
child: Icon(Icons.bluetooth),
backgroundColor: Colors.blue,
onPressed: () {})
],
);
}
}
Notemos que el onPressed
lo tenemos sin acción, más adelante agregaremos su funcionalidad.
Ahora dentro de lib/widgets
crearemos otro archivo llamado body, lib/widgets/body.dart
para crear el widget
con el texto centralizado.
import 'package:flutter/material.dart';
class Body extends StatelessWidget {
Widget build(BuildContext context) {
(context);
return Center(
child: Text("",
style: TextStyle(fontSize: 20),
),
);
}
}
El texto mostrado por ahora es la cadena vacía.
Crearemos otro widget
dentro de lib, llamado home lib/Home.dart
:
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(child: Text("")),
),
body: Body(),
floatingActionButton: FloatingAction(),
);
}
}
Aquí estamos renderizando un Scaffold
, con un AppBar
y su título vacío por el momento, además renderizamos los widgets
Body
y FloatingAction
creados anteriormente.
Nada de Provider
aún, solo vamos armando la interfaz gráfica de la app, pasemos a modificar el main.dart
.
Nuestro archivo main.dart
debería lucir así por el momento:
import 'package:flutter/material.dart';
import 'package:provider_pattern/home.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Provider',
home: Home()
);
}
}
Con esto tenemos todo listo para comenzar a construir nuestro Provider
; hagamos lo siguiente:
Dentro de lib creamos otro directorio llamdo provider y dentro un archivo llamado signal_mode.dart, lib/provider/signal_mode.dart
import 'package:flutter/material.dart';
class SignalMode extends ChangeNotifier {
String _signalMode = 'Wifi mode';
String get signalMode => _signalMode;
set signalMode(String mode) {
this._signalMode = mode;
notifyListeners();
}
}
Listo, este es nuestro Provider
, es la clase que se ocupa de centralizar el valor de la variable privada _signalMode
de tipo String
que es el texto que se mostrará tanto en el título del AppBar
como en el centro de la pantalla.
Notemos el primer detalle que extiende de ChangeNotifier
clase encargada de exponer los eventos de notificación,
veamos que tenemos un método get
para obtener el valor de _signalMode
, y un método set
para modificarlo, dentro de este método vemos la llamada a notifyListeners()
que es quien va a notificar a todos los widgets
que escuchen a este provider del cambio, para asi actualizar su estado.
Es momento de encapsular aquellos widgets
que van a escuhar los cambios.
Como queremos tener el control del estado en todos los niveles de la aplicación lo haremos en el main
.
Tenga en cuenta que no necesariamente se debe envolver en el nivel más alto de la aplicación en caso que desees compartir determinado estado para determinado nivel.
Si modificamos nuestro main
quedaría de la siguiente forma:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_pattern/home.dart';
import 'package:provider_pattern/provider/signal_mode.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => SignalMode(),
child: MaterialApp(
title: 'Provider',
home: Home()),
);
}
}
De esta manera toda la app está en condiciones de escuchar los cambios de la clase SignalMode
Desde ahora ya podemos integrar los distintos widgets
de nuestra app. Modifiquemos la clase FloatingAction
para conectarla con nuestro Provider
quedando de la siguiente forma:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_pattern/provider/signal_mode.dart';
class FloatingAction extends StatelessWidget {
Widget build(BuildContext context) {
final signal = Provider.of<SignalMode>(context);
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
child: Icon(Icons.wifi),
backgroundColor: Colors.orange,
onPressed: () {
signal.signalMode = "Wifi mode";
}),
SizedBox(
height: 10.0,
),
FloatingActionButton(
child: Icon(Icons.bluetooth),
backgroundColor: Colors.blue,
onPressed: () {
signal.signalMode = "Bluetooth mode";
})
],
);
}
}
Notemos aquí en la siguiente línea:
final signal = Provider.of<SignalMode>(context);
de esta manera obtenemos una instancia de nuestro Provider
especificando que es de tipo SignalMode
.
El otro punto a notar aquí es en el onPressed
de los botones, cada uno hace una llamada modificando el valor de signalMode
onPressed: () {
signal.signalMode = "Wifi mode";
})
onPressed: () {
signal.signalMode = "Bluetooth mode";
})
De esta manera podemos modifcar el estado global, ahora solamente debemos mostrar los valores, veamos como:
import 'package:flutter/material.dart';
import 'package:provider_pattern/provider/signal_mode.dart';
import 'package:provider_pattern/widgets/body.dart';
import 'package:provider_pattern/widgets/floating_actions.dart';
import 'package:provider/provider.dart';
class Home extends StatelessWidget {
Widget build(BuildContext context) {
final signal = Provider.of<SignalMode>(context);
return Scaffold(
appBar: AppBar(
title: Center(child: Text(signal.signalMode)),
),
body: Body(),
floatingActionButton: FloatingAction(),
);
}
}
En la clase Home
igualmente obtenemos la instancia del Provider
y leemos la propiedad signalHome
.
De la misma forma lo hacemos en la clase Body
:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_pattern/provider/signal_mode.dart';
class Body extends StatelessWidget {
Widget build(BuildContext context) {
final signal = Provider.of<SignalMode>(context);
return Center(
child: Text(
signal.signalMode,
style: TextStyle(fontSize: 20),
),
);
}
}
Ya, es todo!!
Hemos finalizado nuestro ejemplo, pero les dejaré una última consideración para la utilización de múltiples providers tendríamos que modificar nuevamente nuestro main
de la siguiente forma:
void main() => runApp(AppState());
class AppState extends StatelessWidget {
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => SignalMode())
],
child: MyApp(),
);
}
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Provider',
home: Home()
);
}
}
Percatarse aquí como MultiPorvider
admite un arreglo de Provider
:
providers: [
ChangeNotifierProvider(create: (_) => SignalMode())
],
Hasta aquí este post, espero haberme explicado bien y de alguna manera haberle sido útil, nos vemos en la próxima!!
Chao Mundo!!
17