Providers
Ahora que hemos instalado Riverpod, hablemos de los "providers".
Los providers son la parte más importante de una aplicación de Riverpod. Un provider es un objeto que encapsula un estado y permite escuchar ese estado.
¿Por qué usar providers?
Al envolver una parte del estado en un provider, esto:
Permite acceder fácilmente a ese estado en múltiples ubicaciones. Los providers son un reemplazo completo para patrones como Singletons, Service Locators, Dependency Injection o InheritedWidgets.
Simplifica combinar este estado con otros. ¿Alguna vez te costó fusionar varios objetos en uno solo? Este escenario se construye directamente dentro de los providers, con una sintaxis simple.
Permite optimizaciones de rendimiento. Ya sea para filtrar reconstrucciones de widgets o para almacenar en caché demandantes cálculos de estado; los providers se aseguran de que solo se vuelva a calcular lo que se ve afectado por un cambio de estado.
Aumenta la capacidad de prueba de su aplicación. Con los providers, no necesita pasos complejos de
setUp
/tearDown
. Además, se puede anular cualquier provider para que se comporte de manera diferente durante los test, lo que permite testear fácilmente un comportamiento muy específico.Integre fácilmente con funciones avanzadas, como logging o pull-to-refresh.
Creando un provider
Los providers vienen en muchas variantes, pero todos funcionan de la misma manera.
El uso más común es declararlos como constantes globales así:
final myProvider = Provider((ref) {
return MyValue();
});
No tengas miedo por el aspecto global de los providers. Los providers son totalmente inmutables. Declarar un provider no es diferente de declarar una función, y es comprobable y mantenible.
Este fragmento de código consta de tres componentes:
final myProvider
, la declaración de una variable. Esta variable es la que usaremos en el futuro para leer el estado de nuestro provider. Siempre debe ser inmutable.Provider
, el provider que decidimos utilizar. Provider es el más básico de todos los providers. Expone un objeto que nunca cambia. Podríamos reemplazar Provider con otros providers como StreamProvider o StateNotifierProvider, para cambiar la forma en que se interactúa con el valor.Una función que crea el estado compartido. Esa función siempre recibirá un objeto llamado
ref
como parámetro. Este objeto nos permite leer otros providers o realizar algunas operaciones cuando el estado de nuestro provider será destruido.
El tipo de objeto creado por la función pasada a un provider depende del provider utilizado. Por ejemplo, la función de un Provider puede crear cualquier objeto. Por otro lado, se espera que el callback de StreamProvider devuelva un Stream.
Puedes declarar tantos providers como quieras sin limitaciones.
A diferencia de cuando usamos el package:provider
,
en Riverpod podemos hacer que dos providers expongan un estado del mismo "tipo":
final cityProvider = Provider((ref) => 'London');
final countryProvider = Provider((ref) => 'England');
El hecho de que ambos providers creen un String
no supone ningún problema.
Para que los providers funcionen, debe agregar ProviderScope en la raíz de sus aplicaciones de Flutter:
void main() {
runApp(ProviderScope(child: MyApp()));
}
Tipos diferentes de Providers
Hay múltiples tipos de providers para diferentes casos de uso.
Con todos estos providers disponibles, a veces es difícil entender cuándo utilizar un tipo de provider sobre otro. Utiliza la tabla a continuación para elegir un provider que se adapte a lo que deseas proporcionar al árbol de widgets.
Tipo de Provider | Función de creación de Provider | Ejemplo de caso de uso |
---|---|---|
Provider | Retorna cualquier tipo | Una clase de servicio / propiedad calculada (lista filtrada) |
StateProvider | Retorna cualquier tipo | Una condición de filtro / objeto de estado simple |
FutureProvider | Retorna un Future de cualquier tipo | Un resultado de una llamada a una API |
StreamProvider | Retorna un Stream de cualquier tipo | Un stream de los resultados de una API |
StateNotifierProvider | Retorna una subclase de StateNotifier | Un objeto de estado complejo que es inmutable excepto a través de una interfaz |
ChangeNotifierProvider | Retorna una subclase de ChangeNotifier | Un objeto de estado complejo que requiere mutabilidad |
Aunque todos los proveedores tienen su propósito, los ChangeNotifierProvider no se recomiendan para aplicaciones escalables debido a problemas con el estado mutable. Existe en el paquete flutter_riverpod
para proporcionar una ruta de migración fácil desde package:provider
, y permite algunos casos de uso específicos de Flutter, como la integración con algunos paquetes de Navigator 2
Realizando acciones antes de la destrucción del estado
En algunos casos, el estado de un provider puede destruirse o recrearse.
Un caso de uso común en esas situaciones es realizar una limpieza antes de que se
destruya el estado de un provider, como cerrar un StreamController
.
Esto se hace usando el objeto ref
, que se pasa al callback
de
todos los providers, usando su método onDispose.
El siguiente ejemplo usa onDispose para cerrar un StreamController
:
final example = StreamProvider.autoDispose((ref) {
final streamController = StreamController<int>();
ref.onDispose(() {
// Cierra el StreamController cuando se destruye el estado de este provider.
streamController.close();
});
return streamController.stream;
});
Dependiendo del provider utilizado, es posible que ya se encargue del proceso de limpieza.
Por ejemplo, StateNotifierProvider llamará al método dispose
de un StateNotifier
.
Modificadores de Providers
Todos los providers tienen una forma integrada de agregar funcionalidades adicionales a sus diferentes providers.
Pueden agregar nuevas funciones al objeto ref
o cambiar ligeramente la forma en que se consume el provider.
Los modificadores se pueden usar en todos los providers, con una sintaxis similar a los constructores nombrados:
final myAutoDisposeProvider = StateProvider.autoDispose<String>((ref) => 0);
final myFamilyProvider = Provider.family<String, int>((ref, id) => '$id');
Por el momento, hay dos modificadores disponibles:
.autoDispose
, lo que hará que el provider destruya automáticamente su estado cuando ya no se escuche..family
, lo que permite crear un provider a partir de parámetros externos.
Un provider puede usar múltiples modificadores a la misma vez:
final userProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
return fetchUser(userId);
});
¡Eso es todo por esta guía!
Puede continuar con Cómo leer un provider. Alternativamente, puede ver Cómo combinar providers.