22
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
Esta app muestra una pantalla con un
Tenemos en cuenta que el valor del texto será administrado de manera global.
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
https://github.com/hextiandro/flutter_provider_pattern
Primeramente instalaremos la biblioteca:
La podemos encontrar en: https://pub.dev/packages/provider
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
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
Nada de
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
veamos que tenemos un método
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
Como queremos tener el control del estado en todos los niveles de la aplicación lo haremos en el
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
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!!
22