En esta guía se mostrará el uso de Provider para el manejo de estados en Flutter.
El código de esta guía se lo puede encontrar en GitHub.
marcedroid / Flutter-State-Management-Demos
Manejo de estados en Flutter
Importante
La rama provider es la que tiene la implementación de esta guía.
La rama master tiene el código base de la aplicación.
La aplicación cuenta con tres vistas:
User: En esta vista al cambiar los valores del campo de texto también se actualizará el título del AppBar, además se actualizará en nombre de usuario en la vista cart.
Catalog: En esta vista se muestra un listado de Items y al interactuar con cada uno de ellos se puede ver que el ícono para agregar/eliminar cambia, también se actualiza el contador de items del AppBar y el listado de items de la vista cart.
Cart: En esta vista se muestra un listado de los productos que se han seleccionado en la vista de catalog también se muestra el precio total y el nombre de usuario junto con las iniciales del mismo en un CircleAvatar.
Para mantener el código ordenado se creó la carpeta providers y dentro de esta los archivos catalog_provider.dart, user_provider.dart
- providers
- catalog_provider.dart
- user_provider.dart
Índice
Instalar Provider
https://pub.dev/packages/provider
Para este ejemplo se ha utilizado la versión 4.3.2+2
pubspec.yaml
dependencies:
provider: ^4.3.2+2
Provider para nombre de usuario
user_provider.dart
import 'package:flutter/material.dart';
class UserProvider with ChangeNotifier {
String _username = 'Guest';
String get username => _username;
void onChange(value) {
_username = value;
notifyListeners();
}
}
Explicación:
Importar la librería material.dart ya que se va a usar como mixin ChangeNotifier en la clase UserProvider
import 'package:flutter/material.dart';
Crear la clase UserProvider y utilizar como mixin la clase ChangeNotifier
class UserProvider with ChangeNotifier {
...
}
Crear una variable privada que va a contener el nombre de usuario, también agregar un getter para que se pueda obtener este valor fuera de la clase UserProvider.
String _username = 'Guest';
String get username => _username;
Crear un método que va a actualizar el nombre de usuario y a su vez va a notificar los cambios.
void onChange(value) {
_username = value;
// Notificar los cambios con notifyListeners();
notifyListeners();
}
Provider para catálogo
catalog_provider.dart
import 'package:flutter/material.dart';
import 'package:state_management/models/item_model.dart';
class CatalogProvider with ChangeNotifier {
List<ItemModel> _catalog = [];
List<ItemModel> get catalog => _catalog;
void addToCatalog(ItemModel itemModel) {
_catalog.add(itemModel);
notifyListeners();
}
void removeFromCatalog(ItemModel itemModel) {
_catalog.remove(itemModel);
notifyListeners();
}
}
Explicación:
Al igual que con user_provider.dart es necesario importar la librería material.dart, pero esta vez también va a ser necesario item_model.dart ya que se van a usar objetos de tipo ItemModel.
import 'package:flutter/material.dart';
import 'package:state_management/models/item_model.dart';
Crear la clase CatalogProvider y utilizar como mixin la clase ChangeNotifier
class CatalogProvider with ChangeNotifier {
...
}
Crear una variable privada que va a contener el listado de items, también agregar un getter para que se pueda obtener este listado fuera de la clase CatalogProvider.
List<ItemModel> _catalog = [];
List<ItemModel> get catalog => _catalog;
Crear un método para agregar un nuevo item al listado y a su vez va a notificar los cambios.
void addToCatalog(ItemModel itemModel) {
_catalog.add(itemModel);
// Notificar los cambios con notifyListeners();
notifyListeners();
}
Crear otro método para eliminar un nuevo item del listado y a su vez va a notificar los cambios.
void removeFromCatalog(ItemModel itemModel) {
_catalog.remove(itemModel);
// Notificar los cambios con notifyListeners();
notifyListeners();
}
Modificar main.dart
import 'package:provider/provider.dart';
import 'package:state_management/providers/catalog_provider.dart';
import 'package:state_management/providers/user_provider.dart';
...
void main() => runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserProvider()),
ChangeNotifierProvider(create: (_) => CatalogProvider())
],
child: MyApp(),
),
);
Explicación:
Importar la librería provider.dart además de los archivos catalog_provider.dart y user_provider.dart.
import 'package:provider/provider.dart';
import 'package:state_management/providers/catalog_provider.dart';
import 'package:state_management/providers/user_provider.dart';
Modificar el método original void main() => runApp(MyApp());
Porque se deben crear los providers el nivel más alto posible en la jerarquía de widgets.
void main() => runApp(
// Agregar un MultiProvider ya que se van a utilizar varios providers
MultiProvider(
providers: [
// Crear el CatalogProvider
ChangeNotifierProvider(create: (_) => UserProvider()),
// Crear el UserProvider
ChangeNotifierProvider(create: (_) => CatalogProvider())
],
// Pasar MyApp() como widget hijo
child: MyApp(),
),
);
Vistas
En esta sección se mostrará los cambios necesarios a realizar para que las vistas se actualicen usando providers.
user.dart
import 'package:provider/provider.dart';
import 'package:state_management/providers/user_provider.dart';
...
@override
void initState() {
super.initState();
_textEditingController =
TextEditingController(text: context.read<UserProvider>().username);
}
...
final _userProvider = Provider.of<UserProvider>(context);
...
title: Text('User - ${_userProvider.username}'),
...
onChanged: (value) {
_userProvider.onChange(value);
},
Explicación:
Importar la librería provider.dart y el archivo user_provider.dart
import 'package:provider/provider.dart';
import 'package:state_management/providers/user_provider.dart';
Sobrescribir el método initState
para pasar el nombre de usuario al TextEditingController, esto se hace para tener un valor por default en el campo de texto.
@override
void initState() {
super.initState();
// Obtener el nombre de usuario fuera de una vista con
// context.read<UserProvider>().username
_textEditingController =
TextEditingController(text: context.read<UserProvider>().username);
}
Agregar una variable privada en el método build
que contenga UserProvider
final _userProvider = Provider.of<UserProvider>(context);
Agregar el nombre de usuario al título del AppBar usando _userProvider.username
title: Text('User - ${_userProvider.username}'),
Llamar el método onChange
de UserProvider al momento de hacer un cambio en el campo de texto.
onChanged: (value) {
_userProvider.onChange(value);
},
catalog.dart
import 'package:provider/provider.dart';
import 'package:state_management/providers/catalog_provider.dart';
...
child: CircleAvatar(
child: Text(
'${context.watch<CatalogProvider>().catalog.length}',
...
@override
Widget build(BuildContext context) {
final _catalogProvider =
Provider.of<CatalogProvider>(context, listen: false);
...
trailing: IconButton(
onPressed: () {
if (items[index].addedToCart) {
_catalogProvider.removeFromCatalog(items[index]);
} else {
_catalogProvider.addToCatalog(items[index]);
}
Explicación:
Importar la librería provider y el archivo catalog_provider.dart
import 'package:provider/provider.dart';
import 'package:state_management/providers/catalog_provider.dart';
Actualizar el número de items en el AppBar utilizando context.watch
child: CircleAvatar(
child: Text(
'${context.watch<CatalogProvider>().catalog.length}',
Crear una variable privada de tipo provider, pero creada con el parámetro listen
en false, ya que se va a utilizar para actualizar datos del provider no para obtenerlos.
@override
Widget build(BuildContext context) {
final _catalogProvider =
Provider.of<CatalogProvider>(context, listen: false);
Utilizar _catalogProvider
para remover o agregar items al listado de items.
trailing: IconButton(
onPressed: () {
if (items[index].addedToCart) {
_catalogProvider.removeFromCatalog(items[index]);
} else {
_catalogProvider.addToCatalog(items[index]);
}
cart.dart
import 'package:state_management/providers/catalog_provider.dart';
import 'package:state_management/providers/user_provider.dart';
import 'package:provider/provider.dart';
...
@override
Widget build(BuildContext context) {
final _userProvider = Provider.of<UserProvider>(context);
final _catalogProvider = Provider.of<CatalogProvider>(context);
...
child: ListView.builder(
itemCount: _catalogProvider.catalog.length,
itemBuilder: (context, index) => ListTile(
title: Text(
'${_catalogProvider.catalog[index].name}'.toUpperCase(),
style: TextStyle(fontWeight: FontWeight.bold),
),
trailing: Text(
'\$${_catalogProvider.catalog[index].price.toStringAsFixed(2)}',
),
),
),
...
Column(
children: [
CircleAvatar(
child: Text(
getInitials(_userProvider.username).toUpperCase()),
),
Text(
'${_userProvider.username}',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
],
),
...
Text(
'\$${formatTotal(_catalogProvider.catalog)}',
Explicación:
Importar la librería provider y los archivos catalog_provider.dart y user_provider.dart
import 'package:state_management/providers/catalog_provider.dart';
import 'package:state_management/providers/user_provider.dart';
import 'package:provider/provider.dart';
Crear dos variables privadas, para obtener los datos del provider de user y catalog.
@override
Widget build(BuildContext context) {
final _userProvider = Provider.of<UserProvider>(context);
final _catalogProvider = Provider.of<CatalogProvider>(context);
Actualizar ListView.builder
child: ListView.builder(
// Actualizar el contador de items.
itemCount: _catalogProvider.catalog.length,
itemBuilder: (context, index) => ListTile(
title: Text(
// Mostrar el nombre del item.
'${_catalogProvider.catalog[index].name}'.toUpperCase(),
style: TextStyle(fontWeight: FontWeight.bold),
),
trailing: Text(
// Mostrar el precio del producto con dos decimales.
'\$${_catalogProvider.catalog[index].price.toStringAsFixed(2)}',
),
),
),
Actualizar el nombre de usuario.
Column(
children: [
CircleAvatar(
child: Text(
// Mostrar las iniciales del nombre de usuario
getInitials(_userProvider.username).toUpperCase()),
),
Text(
// Mostrar el nombre de usuario
'${_userProvider.username}',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
],
),
Actualizar el valor total de los items.
Text(
'\$${formatTotal(_catalogProvider.catalog)}',
Eso es todo, recuerda que código de esta guía se lo puede encontrar en GitHub.
Top comments (0)