Manejadores de estados en Flutter: setState, BLoC, ValueNotifier, Provider

Carlos Millan
27 julio, 2019

. . .

Traducción al español del artículo en inglés escrito por Andrea Bizzotto.

Este artículo es una reseña de los puntos destacados de este video, donde comparamos diferentes técnicas de administración de estado.

Como ejemplo, usamos un flujo de autenticación simple. Esto establece un estado de carga mientras una solicitud de inicio de sesión está en curso.

Para simplificar, este flujo se compone de tres estados posibles:

Estos están representados por la siguiente máquina de estado, que incluye un estado de loading y un estado de autenticación:

Cuando una solicitud de inicio de sesión está en progreso, deshabilitamos el botón de inicio de sesión y mostramos un indicador de progreso.

Esta aplicación de ejemplo muestra cómo manejar el estado de carga con varias técnicas de manejadores de estado.

Navegación Principal

La navegación principal para la página de inicio de sesión se implementa con un widget que utiliza un menú Drawer para elegir entre diferentes opciones:

El código para esto es el siguiente:

Este widget muestra un Scaffold donde:

  • El título del AppBar es el nombre de la opción seleccionada.
  • El Drawer usa un MenuSwitcher personalizado.
  • El body usa un switch para escoger entre diferentes páginas.

Flujo de Referencia (Vanilla)

Para habilitar el inicio de sesión, podemos comenzar con una implementación de vanilla simple que no tiene un estado de carga:

Cuando el SignInButton es presionado, llamamos al método _signInAnonymously.

Este usa Provider para obtener un objeto AuthService y lo usa para iniciar sesión.

NOTAS:

  • AuthService es un contenedor simple para la autenticación de Firebase. Vea este artículo para más detalles.
  • El estado de autenticación es manejado por un widget ancestro, que usa la secuencia onAuthStateChanged para decidir qué página mostrar. Cubrí esto en un artículo anterior.

setState

El estado de carga se puede agregar a la implementación anterior:

  • Convirtiendo nuestro widget a un StatefulWidget
  • Declarando una variable de estado local
  • Usándolo dentro de nuestro método build
  • Actualizándolo antes y después de la llamada a sign in.

Este es el código resultante:

Top Tip: Nótese cómo usamos una cláusula finally. Esto se puede usar para ejecutar algún código, ya sea que se haya lanzado una excepción o no.

BLoC

El estado de carga puede ser representado por los valores de un stream dentro de un BLoC.

Y necesitamos algo de código adicional para configurar las cosas:

En pocas palabras, este código:

  • Añade un SignInBloc con un StreamController<Bool> que esusado para manejar el estado de carga.
  • Hace el SignInBloc accesible a nuestro widget con un par Provider/Consumer dentro de un método create estático.
  • llama a bloc.setIsLoading(value) para actualizar el stream, dentro del método _signInAnonymously
  • Recibe el estado de carga mediante un StreamBuilder, y lo usa para configurar el botón sign-in.

Nota Acerca de RxDart

BehaviourSubjet es un controlador de stream especial que nos da acceso síncrono al último valor del stream.

Como una alternativa a BLoC, podríamos usar un BehaviourSubjet para mantener el trazo del estado de carga, y actualizarlo si es necesario.

Actualizaré el proyecto en GitHub para mostrar como hacer esto.

ValueNotifier

Un ValueNotifier puede ser usado para mantener un valor simple, y notificar a sus listeners cuando este cambie.

Este es usado para implementar el mismo flujo:

Deontro del método estático create, usamos un ChangeNotifierProvider / Consumer con un ValueNotifier<Bool>. Esto nos da una forma de representar el estado de carga, y reconstruir el widget cuando este cambie.

ValueNotifier vs ChangeNotifier

ValueNotifier y ChangeNotifier están cercanamente relacionadas.

De hecho, ValueNotifier es una subclase de ChangeNotifier que implementa ValueListenable<T>.

Esta es la implementación de ValueNotifier en el SDK de Flutter:

Entonces, cuando deberíamos usar ValueNotifier vs ChangeNotifier?

  • Use ValueNotifier si usted necesita reconstruir los widgets cuando un valor simple cambie.
  • Use ChangeNotifier si usted quiere más control cuando notifyListeners() es llamado.

Nota Acerca de ScopedModel

ChangeNotifierProvider es muy similar a ScopedModel. De hecho, estos pares son también equivalentes:

  • ScopeModel <–> ChangeNotifierProvider
  • ScopeModelDescendant <–> Consumer

Por lo tanto, no necesita ScopedModel si ya está utilizando Provider, ya que ChangeNotifierProvider ofrece la misma funcionalidad.

Comparación Final

Las tres implementaciones (setState, BLoC, ValueNotifier) son muy similares, y únicamente difieren en cómo el estado de carga es manejado.

Aquí es cómo se comparan:

  • setState <–> menos cantidad de código
  • BLoC <–> mucha cantidad de código
  • ValueNotifier <–>punto medio

Por lo tanto, setState funciona mejor para este caso de uso, ya que necesitamos manejar el estado que es local para un solo widget.

Puede evaluar cuál es el más adecuado según cada caso, a medida que crea sus propias aplicaciones.

Bonus: Implementando el Menú Drawer

Realizar un seguimiento de la opción seleccionada actualmente también es un problema de administración de estado:

Primero implementé esto con una variable de estado local y setState, dentro del menú drawer personalizado.

Sin embargo, el estado se perdió después del inicio de sesión, porque el drawer se eliminó del árbol de widgets.

Como solución, decidí guardar el estado con un ChangeNotifierProvider > dentro de LandingPage:

Aquí, el StreamBuilder controla el estado de autenticación del usuario.

Y al envolver esto con un ChangeNotifierProvider >, puedo conservar la opción seleccionada incluso después de que se elimine SignInPageNavigation.

En resumen:

  • Los StatefulWidgets no recuerdan su estado después de que se eliminan.
  • Con Provider, podemos elegir dónde almacenar el estado en el árbol de widgets.
  • De esta manera, el estado se mantiene incluso cuando se eliminan los widgets que lo utilizan.

ValueNotifier requiere un poco más de código que setState. Pero se puede usar para recordar el estado, colocando un Proveedor donde corresponda en el árbol de widgets.

Código Fuente

El código de ejemplo de este tutorial se puede encontrar aquí:

Todas estas técnicas de administración del estado se cubren en profundidad en mi curso de Flemter y Firebase Udemy. Esto está disponible para acceso temprano en este enlace (código de descuento incluido):

Happy coding!

1

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Comunidades en Español