Saltar al contenido principal

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();
});
NOTA

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.

info

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.

PRECAUCIÓN

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 ProviderFunción de creación de ProviderEjemplo de caso de uso
ProviderRetorna cualquier tipoUna clase de servicio / propiedad calculada (lista filtrada)
StateProviderRetorna cualquier tipoUna condición de filtro / objeto de estado simple
FutureProviderRetorna un Future de cualquier tipoUn resultado de una llamada a una API
StreamProviderRetorna un Stream de cualquier tipoUn stream de los resultados de una API
StateNotifierProviderRetorna una subclase de StateNotifierUn objeto de estado complejo que es inmutable excepto a través de una interfaz
ChangeNotifierProviderRetorna una subclase de ChangeNotifierUn objeto de estado complejo que requiere mutabilidad
caution

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;
});
NOTA

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.
NOTA

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.