Cómo manejar el estado en Flutter usando el patrón BLoC

El año pasado, compré Flutter y debo decir que ha sido un viaje increíble hasta ahora. Flutter es el increíble marco de trabajo de Google para crear aplicaciones de alta calidad para Android e iOS.

Al igual que con la construcción de casi cualquier aplicación, siempre existe la necesidad de manejar el estado de la aplicación. Es importante que la administración estatal se maneje de manera eficiente, para evitar acumular deudas técnicas, especialmente a medida que su aplicación crece y se vuelve más compleja.

En Flutter, todos los componentes de la interfaz de usuario son widgets. Cuando comiences a componer estos widgets para crear tu increíble aplicación, terminarás con un árbol de widgets profundamente anidados. Es muy probable que estos widgets necesiten compartir el estado de la aplicación entre sí.

En este artículo, veremos cómo manejar el estado en Flutter usando el patrón BLoC.

La gestión del estado en Flutter se puede lograr de diferentes maneras:

Widget heredado : le permite propagar datos a sus widgets secundarios y los widgets se reconstruyen cada vez que hay un cambio en el estado de la aplicación. La desventaja de usar la clase base InheritedWidget es que su estado es final y esto plantea un problema si desea mutar su estado.

Modelo con alcance : este es un paquete externo construido sobre InheritedWidget y ofrece una forma ligeramente mejor de acceder, actualizar y mutar el estado. Le permite pasar fácilmente un modelo de datos de un widget principal a sus descendientes. Además, también reconstruye todos los elementos secundarios que usan el modelo cuando se actualiza el modelo.

Esto puede generar un problema de rendimiento, dependiendo de cuántos ScopedModelDescendants tenga un modelo, ya que se reconstruyen cuando hay una actualización.

Este problema se puede solucionar descomponiendo ScopedModel en varios modelos para que obtenga dependencias más detalladas. Establecer la rebuildOnChangebandera en falsetambién soluciona este problema, pero trae consigo la carga cognitiva de decidir qué widget debe reconstruirse o no.

Redux : ¡Sí! Al igual que con React, hay un paquete Redux que te ayuda a crear y consumir fácilmente una tienda Redux en Flutter. Al igual que su contraparte de JavaScript, generalmente hay algunas líneas de código repetitivo y el viaje de ida y vuelta de acciones y reductores .

Ingrese el patrón BLoC

El patrón Business Logic Component (BLoC) es un patrón creado por Google y anunciado en Google I / O '18. El patrón BLoC usa la programación reactiva para manejar el flujo de datos dentro de una aplicación.

Un BLoC es un intermediario entre una fuente de datos en su aplicación (por ejemplo, una respuesta de API) y los widgets que necesitan los datos. Recibe flujos de eventos / datos de la fuente, maneja cualquier lógica empresarial requerida y publica flujos de cambios de datos en los widgets que están interesados ​​en ellos.

Un BLoC tiene dos componentes simples: Sinks y Streams , ambos proporcionados por un StreamController . Usted agrega flujos de entrada de datos / eventos en un receptor y los escucha como flujos de salida de datos a través de un flujo .

Se puede acceder a StreamController a través de la ‘dart:async’biblioteca o como PublishSubject , ReplaySubject o BehaviourSubject a través del rxdartpaquete.

A continuación se muestra un fragmento de código que muestra un BLoC simple:

import 'dart:async'; // import 'package:rxdart/rxdart.dart'; if you want to make use of PublishSubject, ReplaySubject or BehaviourSubject. // make sure you have rxdart: as a dependency in your pubspec.yaml file to use the above import class CounterBloc { final counterController = StreamController(); // create a StreamController or // final counterController = PublishSubject() or any other rxdart option; Stream get getCount => counterController.stream; // create a getter for our Stream // the rxdart stream controllers returns an Observable instead of a Stream void updateCount() { counterController.sink.add(data); // add whatever data we want into the Sink } void dispose() { counterController.close(); // close our StreamController to avoid memory leak } } final bloc = CounterBloc(); // create an instance of the counter bloc //======= end of CounterBloc file //======= somewhere else in our app import 'counter_bloc.dart'; // import the counter bloc file here @override void dispose() { bloc.dispose(); // call the dispose method to close our StreamController super.dispose(); } ... @override Widget build(BuildContext context) { return StreamBuilder( // Wrap our widget with a StreamBuilder stream: bloc.getCount, // pass our Stream getter here initialData: 0, // provide an initial data builder: (context, snapshot) => Text('${snapshot.data}'), // access the data in our Stream here ); } ...

Un BLoC es una clase de Dart simple. En el fragmento de código anterior, creamos una CounterBlocclase y en ella, a la StreamControllerque llamamos counterController. Creamos un captador para nuestro Stream llamado getCount, un updateCountmétodo que agrega datos a nuestro Sink cuando se llama y un disposemétodo para cerrar nuestro StreamController.

Para acceder a los datos en nuestro Stream, creamos un StreamBuilderwidget y pasamos nuestro Stream a su streampropiedad y accedimos a los datos en su builderfunción.

Implementación de BLoC

Convertiremos la aplicación de muestra predeterminada de Flutter para usar un BLoC. Sigamos adelante y generemos una nueva aplicación Flutter. En su terminal, ejecute el siguiente comando:

$ flutter create bloc_counter && cd bloc_counter

Abra la aplicación en su editor favorito y crear tres archivos en la carpeta lib: counter.dart, counter_provider.darty counter_bloc.dart.

Nuestro CounterProvidercontendrá un número entero y un método para incrementarlo. Agregue el siguiente código al counter_provider.dartarchivo:

class CounterProvider { int count = 0; void increaseCount() => count++; }

A continuación, implementaremos nuestro contador BLoC. Agregue el siguiente código en su counter_block.dartarchivo:

En nuestra CounterBlocclase, usamos parte de nuestro código de muestra inicial anterior. En la línea 7, creamos una instancia de nuestra CounterProviderclase y en el updateCountmétodo, llamamos al método del proveedor para incrementar el conteo, y luego en la línea 13, pasamos el conteo a nuestro Sink.

Reemplace el código en su main.dartarchivo con el código siguiente. En el siguiente código, simplemente eliminamos la mayor parte del código de contador predeterminado, que trasladaremos a nuestro counter.dartarchivo. Siempre incrementCounterque se llama al método, llamamos al updateCountmétodo de BLoC que actualiza el recuento y lo agrega a nuestro Sink.

Ahora, nuestro BLoC está recibiendo y transmitiendo datos. Podemos acceder a esos datos y mostrarlos en una pantalla a través de StreamBuilder . Envolvemos cualquier widget que necesite los datos en un widget de StreamBuilder y le pasamos el flujo que contiene los datos. Agregue el siguiente código al counter.dartarchivo:

En el código anterior, tenemos un widget con estado. En nuestra clase de estado, en la línea 13, llamamos al método dispose de nuestro bloque, para que el controlador de flujo se pueda cerrar cada vez que se elimine el widget del árbol.

En la línea 19, devolvemos un widget StreamBuilder y la línea 20, le pasamos el captador de nuestro flujo y también un dato inicial en la línea 21. El StreamBuilder también tiene un builderque nos da acceso a los datos a través de a snapshot. En la línea 30 accedemos y mostramos los datos en la instantánea.

Continúe y ejecute la aplicación ejecutando el siguiente comando. Asegúrese de tener un emulador en ejecución.

$ flutter run

Con su aplicación en ejecución, haga clic en el icono más y observe cómo aumenta el contador con cada clic.

Juntos, hemos podido implementar la forma más simple de BLoC en Flutter. El concepto sigue siendo el mismo independientemente de su caso de uso.

Espero que este artículo le haya resultado útil. Hazlo y compártelo para que otros puedan encontrar este artículo. Contáctame en Twitter @developia_ con preguntas o para charlar.