Simplificando la gestión de estado con Bloc en Flutter
Lograr un código más limpio en Flutter con Bloc
Hola amigos, luego de un tiempo de no hacer una traducción para la comunidad de Flutter en español, les traigo un nuevo artículo escrito por Dhananjay Trivedi, que puedes encontrar el artículo original aquí.
Si ha estado desarrollando aplicaciones Flutter, sabe que es crucial administrar el estado de su aplicación de la mejor manera posible.
Flutter nos brinda muchas opciones de administración de estado, como Provider, Bloc, Redux y MobX.
Hoy, verás a Bloc.
Entiendo que puede ser un poco más difícil de entender, también lo fue para mí. Pero ahora creo que lo entiendo, así que estoy aquí para simplificartelo.
Sabes que en Flutter es muy fácil mezclar su lógica de UI con la lógica de su negocio, que no es realmente la mejor manera de desarrollar aplicaciones que se puedan mantener.
Bloc viene al rescate al dar una mejor estructura a tú aplicación, al separar la interfaz de usuario y la lógica de negocios de tú aplicación. Pero antes de comenzar a implementarlo, primero debes comprender …
El artículo va a ser un poco largo, pero como principiante, se te irá aclarando el uso de Bloc al final.
¿Qué es Bloc de todos modos?
Bloc significa lógica de negocio y componente. Es como un recuadro negro que envuelve toda su lógica de negocios (y componentes relacionados) y asigna varios eventos de aplicaciones a estados de aplicaciones.
Si esa definición fue demasiado técnica para ti, trata de entenderla mejor con algunos ejemplos.
Si estás usando una aplicación meteorológica:
Habrá varios eventos en su aplicación, como getWeather()
setLocation()
, y resetWeather()
eso se activará con las acciones del usuario o alguna lógica interna de su aplicación.
Estas acciones actualizarán la interfaz de usuario de su aplicación con un nuevo estado en consecuencia después de que la operación se realice con éxito.
Otro ejemplo: la aplicación de contador Flutter predeterminada
Aquí solo hay un evento, y ese es el incrementCounter
que genera el nuevo estado, que muestra el valor incrementado del contador.
bloc
es el cuadro negro que le dirá qué operación debe realizarse en qué evento y luego qué estado debe generarse después de que se complete la operación.
¿Cómo se define el bloque para su aplicación?
Hay un pequeño precio a pagar
Un inconveniente importante de Bloc es la cantidad de código repetitivo que tienes que escribir, incluso para los cambios más pequeños de la interfaz de usuario.
Ese es el único inconveniente que he visto hasta ahora, pero ciertamente simplifica el código de su aplicación al separar toda la lógica de la interfaz de usuario de la lógica de negocios.
Por lo tanto, puede tomar todo el código repetitivo como una inversión requerida para mantener su código fácil de mantener y comprender con Bloc.
Esto es lo que obtienes
Por ejemplo, supongamos que está creando una aplicación meteorológica con Bloc. Has definido todos sus eventos, estados y su aplicación funciona perfectamente.
Ahora, si tienes que agregar otra función dentro de la misma pantalla, puedes simplemente seguir adelante y definir un nuevo evento y un nuevo estado dentro de su bloc
con un poco de lógica sobre cómo asignar el nuevo evento a un nuevo estado.
Ya terminaste! No tienes que tocar / perturbar otros eventos y estados creados, y simplemente puede agregar su nuevo conjunto de características sin mucho problema.
Esto es similar a lo que el principio abierto-cerrado de los principios SOLID le dicen que hagas.
Lo que estaremos construyendo
Desarrollarás una versión modificada de la aplicación de contador, que te ayudará a obtener una imagen más clara de bloc
.
Habrá cuatro botones:
- Incremento del valor del contador: actualiza instantáneamente el valor del contador
- Disminuir el valor del contador: actualiza instantáneamente el valor del contador
- Generar valor aleatorio: simularemos cierto retraso y luego actualizaremos el valor
Si entendió los requisitos, puedes ver que lo que se ha resaltado anteriormente son eventos, y la explicación a la derecha del colon son los estados.
1. Agregue estas dependencias a su pubspec.yaml
A partir de marzo de 2020, estoy agregando esta dependencia para Flutter.
bloc
es la biblioteca creada por el equipo bloclibrary.dev, y proporciona los fundamentos básicos del patrón bloc
. Pero es un poco más complejo; por lo tanto, obtienes flutter_bloc
creado por el mismo equipo para facilitar las cosas a los desarrolladores de Flutter.
Gracias equipo!
2. Crea tu clase ‘Bloc’
Mi código de UI estará en el CounterScreen
, dentro del archivo counter_screen.dart
.
(Llegaremos a la parte de la interfaz de usuario más adelante).
Cree un nuevo archivo Dart para su clase bloc
con el nombre counter_screen_bloc.dart
. Definirás este archivo de clase bloc
.
Dentro de la pantalla bloc
, definiremos tres cosas:
- Evento: para mostrar todos los eventos posibles
- Estado: para mostrar todos los estados posibles
- Bloque: cómo asignar esos eventos a estados
Estas son las declaraciones de importación requeridas para los próximos pasos.
3. Definir eventos
Dentro del archivo de clase en blanco bloc
, comience definiendo un evento.
He nombrado él CounterScreenEvent
.
Equatable
hace que nuestras clases sean equiparables / comparables, lo cual es necesario para la lógica de mapeo dentro de la clase bloc
.
Equatable
le pide que anule el get props
que hemos hecho aquí.
4. Definir estados
Debajo de estos, definamos los estados a los que se asignará cada uno de estos.
¿Por qué, solo dos estados?
Bueno, el usuario verá los valores de contador incrementados / decrementados en la pantalla, o verá una pantalla de carga mientras intentamos generar un número aleatorio que simule una llamada de red.
El ShowCounterValue
estado tendrá un valor de contador asociado. Por lo tanto, definimos una propiedad en esa clase llamada counterValue
para ayudarlo a pasar el valor del contador a la interfaz de usuario de la aplicación desde Bloc.
5. Finalmente, defina nuestro ‘Bloc’
Sí, finalmente podemos definir nuestro bloc
.
- En la primera línea, tienes
DashboardScreenBloc
, que se extiendeBloc<Event,State>
como has definido anteriormente - Defines una propiedad para el valor de contador llamado
counterValue
, que se utilizará para mantener el estado de nuestro contador - Ahora tienes que definir
initialState
. Cuando elbloc
carga, el estado inicial y se establecerá enShowCounterValue(counterValue)
. - Finalmente, tienes una función
mapEventToState
(que hace exactamente lo que significa el nombre). Si observa la función, verá que está marcada comoasync*
.
Sólo para mantener a todos en la misma página, voy a explicarasync
vs async*
.
Si su función devuelve un valor futuro después de realizar una determinada operación, marcamos esa función como async
. Estas funciones devuelven un valor solo una vez, y su ejecución termina con la instrucción return.
La función marcada como async*
, por otro lado, devuelve una secuencia. Esta función produce valores diferentes y no solo los devuelve. Su ejecución aún continúa, y pueden producir tantos valores como desee.
Es por eso que su salida se llama transmisión.
Terminemos nuestro bloc
mapeando eventos a estados.
A continuación se muestra el código completo de counter_screen_bloc.dart
.
Una cosa importante a saber en bloc,
es que si nextState == currentState
evalúa true
, bloc
ignora la transición de estado.
Si no extiende su clase de estado como Equatable
, todos los estados desde los que yield
llamará serán tratados como únicos.
Si eso se ajusta a sus requisitos, no debería molestarse mucho, pero si existe la posibilidad de que se repitan estados uno tras otro, ¿por qué desperdiciar su rendimiento en la reconstrucción del mismo estado?
Por lo tanto, ampliamos Equatable
y anulamos el método props
.
Si no se anula el método props
, bloc
no sabrá cómo comparar las dos clases de estado y probablemente las tratará de la misma manera. Por lo tanto, no vería que se activara el nuevo estado.
6. Trabajemos en esa interfaz de usuario
Comenzando con main.dart
Como estamos usando un bloc
de nombre CounterScreenBloc
en nuestra interfaz de usuario con nombre CounterScreen
, usamos BlocProvider
y definimos qué se debe proporcionar con qué bloc
.
Aquí está el main.dart
código completo .
Miremos adentro 'counter_screen.dart’
Dentro de la build
función de nuestro CounterScreen
, tenemos que inicializar el bloc
. Del mismo modo, dentro de la función de disposición, tenemos que cerrar nuestro bloc
para evitar pérdidas de memoria.
Es una buena práctica disponer del bloque en la función dispose () del mismo widget cuyo build () puede haberse utilizado para inicializar el bloque.
Aquí está el completo counter_screen.dart
:
¡Eso es! Deberías tener un proyecto funcional.
Aquí está el código para el proyecto completo:
Conclusión
¡Eso es! Eso es todo lo que debes saber para crear una aplicación Flutter con código limpio, todo gracias a Bloc.
Nuevamente, el código repetitivo que ve es una inversión para mantener su código mantenible.
También debido a esto, no recomendaría que lo aplique en todas las pantallas, solo donde hay muchos estados diferentes involucrados.
De lo contrario, Provider es otra gran solución de administración de estado para funciones más simples en su aplicación.