Riverpod es un marco de inyección de dependencia y administración de estado reactivo, utiliza diferentes proveedores para permitirnos acceder y escuchar los cambios de estado en nuestra aplicación, está construido por Remi Rousselet. Si no sabe qué es el estado, le recomiendo que lea este artículo primero, ya que le resultará un poco difícil entender Riverpod sin comprender el «estado» en sí.
¿Qué hace realmente Riverpod?
Riverpod es un ayudante de gestión estatal. Básicamente, hace que nuestro estado (en palabras claras, los valores de nuestras variables) sea accesible en todas las partes de la aplicación, coloca nuestro estado en la parte superior del árbol de widgets y nos permite escuchar esos cambios de estado y actualizar nuestra interfaz de usuario en consecuencia.
Hay muchos tipos de proveedores que ofrece Riverpod. Los revisaremos uno por uno.
¿Qué son los proveedores?
Los proveedores son la parte más importante de una aplicación Riverpod, «Un proveedor es un objeto que encapsula un estado y permite escuchar ese estado».
Tipos de proveedores:
- EstadoProveedor
- FuturoProveedor
- StreamProvider
- Proveedor
¡Empecemos a construir!
1. Agregue Riverpod a nuestra aplicación:
Dart
dependencies: flutter_riverpod: ^0.14.0+3
2. Envolviendo nuestra aplicación:
Para que los proveedores funcionen, debemos agregar ProviderScope en la raíz de nuestras aplicaciones Flutter:
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart'; void main() { runApp(ProviderScope(child: MyApp())); }
3. Cree el archivo proveedores.dart :
Definiremos todos nuestros proveedores globales en este archivo para que sea más fácil mantener el proyecto. Definamos un StateProvider.
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart'; // Instead of String you can use other data types as well, // we can also use custom data types. final userNameProvider=StateProvider<String>((ref) { // we can also return an empty String here, for the sake of simplicity, // let's return a sample name return "SomeName"; });
4. Proveedores de lectura:
Ahora que hemos declarado un StateProvider, aprendamos cómo podemos leer proveedores. Para leer cualquier proveedor, tenemos múltiples widgets, en este artículo, repasaremos ConsumerWidget() y Consumer().
Usando un ConsumerWidget():
Un widget de consumidor es un widget que podemos usar en lugar de nuestro widget Stateful/Stateless . Nos da la capacidad de leer/cambiar los estados de los proveedores y también escucharlos.
Dart
import 'Providers/providers.dart' as providers.dart; //for easy access import 'package:flutter_riverpod/flutter_riverpod.dart'; class Home extends ConsumerWidget { @override Widget build(BuildContext context, ScopedReader watch) { // Listens to the value exposed by userNameProvider // ".state" method lets us get the state easily & directly String name = watch(providers.userNameProvider).state; return MaterialApp( home: Scaffold( appBar: AppBar(title: Center(child: const Text('GFG 🙂 '))), body: Center( // displaying the value child: Text('$name'), ), ), ); } }
Producción:
Bien y bueno. Así que ahora, cada vez que cambie el valor de userNameProvider , Text() se actualizará en consecuencia. Pero, ¿cómo vamos a actualizar el valor de userNameProvider?
5. Actualizar/Cambiar el valor de un StateProvider:
Para cambiar el valor de StateProvider, necesitamos un StateController . Así que vamos a crearlo.
Primero, eliminemos la variable «nombre». ̶
S̶t̶r̶i̶n̶g̶ ̶n̶a̶m̶e̶ ̶=̶ ̶w̶a̶t̶c̶h̶(̶p̶r̶o̶v̶i̶d̶e̶r̶s̶.̶u̶s̶e̶r̶N̶a̶m̶e̶P̶r̶o̶v̶i̶d̶e̶r̶)̶.̶s̶t̶a̶t̶e̶;̶ //removed as with it we can only listen/get the state not change/mutate the state. //let's use this StateController instead. StateController<String> nameController = watch(providers.userNameProvider); // now we can get / set the state using name.state.
En aras de la simplicidad, usemos un FloatingActionButton, y usándolo, intentemos mutar (cambiar) el estado de userNameProvider.
Dart
// for easy access import 'Providers/providers.dart' as providers.dart; class Home extends ConsumerWidget { int n = 0; @override Widget build(BuildContext context, ScopedReader watch) { // Listens to the value exposed by userNameProvider StateController<String> nameController = watch(providers.userNameProvider); // ".state" method lets us get the state easily & directly return MaterialApp( home: Scaffold( floatingActionButton: FloatingActionButton( onPressed: () { n++; nameController.state = // now we can set the state using the state setter. "New Name $n"; }, ), appBar: AppBar(title: Center(child: const Text('GFG 🙂 '))), body: Center( child: Text( // displaying the value '${nameController.state}', ), ), ), ); } }
Producción:
Ahora, cuando presionemos el FloatingActionButton(), veremos que el nombre cambia cada vez con un número diferente. Eso es todo. Usando esta información, podemos usar fácilmente StateProviders, mutar sus valores y escucharlos.
Ahora, repasemos los proveedores asincrónicos.
1. Futuro Proveedor:
Cuando trabajamos con código asincrónico, a menudo usamos algunas API basadas en Future, veamos cómo puede manejar eventos Future usando Riverpod. Para demostrarlo, crearemos un método que devuelva un futuro.
Dart
class FutureClass { // in production, the below method could be any network call Future<int> getData(String para) async { await Future.delayed(Duration(seconds: 5)); return 25; } }
Creando un proveedor para FutureClass:
final futureClass = Provider((ref) => FutureClass());
Ahora, creemos nuestro FutureProvider,
Dart
final response = FutureProvider<int>((ref) async { final client = ref.read(futureClass); return client.getData('some text as a parameter'); });
Ahora, podemos usar ConsumerWidget para escuchar los cambios de estado y actualizar nuestra interfaz de usuario en consecuencia.
Consumir un FutureProvider:
Además de ConsumerWidget(), también tenemos un widget Consumer(). La diferencia es que mientras ConsumerWidget reconstruye toda la pantalla cuando cambia el estado, Consumer() reconstruye solo su elemento secundario, asegurándose de que no tengamos problemas de rendimiento.
Usemos el widget Consumer() para observar (escuchar) los cambios de estado.
Dart
class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("FutureProvider Demo"),), body: Center( child: Column( children: [ Consumer( builder: (context, watch, child) { final futureData = watch(response); }, ), ], ), ), ); } }
Como un futuro puede tener 3 estados, es decir, completado, en progreso y error, tenemos la capacidad de manejar esos estados por separado usando la función .map.
Dart
Consumer( builder: (context, watch, child) { final futureData = watch(response); return futureData.map( ,// have data data: (data) => Text('${data.value}',) ,// in progress loading: (_) => CircularProgressIndicator() // has an error error: (message) => Text(message.error), ); }, ),
Si queremos pasar una variable/objeto al proveedor como argumento, entonces podemos hacer eso como-
Dart
final response= FutureProvider.autoDispose.family<int, String>((ref, i_am_a_param) async { final client = ref.read(futureClass); return client.getData(i_am_a_param); }); // the family modifier is used to pass a String value, // which is used to call the get() method. // The modifier "autoDispose" destroys the state of a provider // when it is no longer used, even when the widget state is not yet dispose
Código completo:
Dart
class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: Text("FutureProvider Demo"),), body: SafeArea( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Consumer( builder: (context, watch, child) { final futureData = watch(providers.response); return futureData.map( data: (data) => Text( '${data.value}', ), // have data loading: (_) => Column( children: [ CircularProgressIndicator(), Text( 'Fetching data', ), ], ), // in progress error: (message) => Text(message.error.toString()), // has an error ); }, ), ], ), ), ), ), ); } }
Producción:
2. Proveedor de transmisión:
A menudo usamos transmisiones en una aplicación flutter, ya sea para obtener datos de Firestore o leer datos de un archivo. Aprendamos cómo podemos manejar esos flujos con Riverpod.
Primero declaremos una corriente,
final streamProvider = StreamProvider<int>((ref) { return Stream.fromIterable([105, 50]); //in production this could be a stream of documents from Firestore });
Consumo de StreamProvider:
Esta vez, usemos ConsumerWidget() para consumir los datos de StreamProvider, es bastante similar al de FutureProvider.
Dart
class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text("StreamProvider Demo"), ), body: SafeArea( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Consumer( builder: (context, watch, child) { final streamValue = watch(providers.streamProvider); return streamValue.when( data: (data) => Text( '${data}', ), // have data loading: () => Column( children: [ CircularProgressIndicator(), Text( 'Fetching data', ), ], ), // in progress error: (message, e) => Text(message.toString()), // has an error ); }, ), ], ), ), ), ), ); } }
Ahora, cada vez que la transmisión tenga datos nuevos, nuestra interfaz de usuario se actualizará en consecuencia.
Observe cómo no tuvimos que administrar diferentes estados por nosotros mismos, sin Riverpod, el código anterior se vería así:
Dart
final stream = Stream.fromIterable([105, 50]); StreamBuilder<int>( stream: stream, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.active) { if (snapshot.hasData) { // data return SomeWidget(snapshot.data); } else if (snapshot.hasError) { // error state return SomeErrorWidget(snapshot.error); } else { // no data return Text('No data'); } } else { // loading state return CircularProgressIndicator(); } } )
Eso es todo. Esto debería cubrir casi todo lo que necesitamos aprender para comenzar con Riverpod.
El código completo para el artículo anterior se puede encontrar aquí .
Publicación traducida automáticamente
Artículo escrito por madhavamshahi12 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA