Gestión de estado con Flutter

Equipo Sofka
20 octubre, 2022
Comparte
Autor: Santiago Rhenals Rodríguez.

Para nadie es un secreto que la comunidad de Flutter, el famoso UI Toolkit de Google, crece exponencialmente, y con ella las librerías con las que podemos manejar el estado compartido de nuestra aplicación. Sin embargo, antes de empezar a discutir las opciones que Flutter y su comunidad nos ofrecen a día de hoy, es importante recordar la definición del estado y cómo la renderización de los widgets que visualizamos en la aplicación es hecha por el framework.

¿Cuál es el estado de una aplicación?

Para poder comprender respecto a lo que es la gestión de estado y los gestores que nos permiten lograrla, necesitamos entender un concepto que encontramos en múltiples páginas de la documentación de Flutter y puede llegar a ser confuso especialmente al momento de empezar a desarrollar aplicaciones multiplataforma con este framework: El estado.

Al leer la documentación oficial de Flutter, podremos encontrar la definición de “estado” descrita de la siguiente manera:

Todos aquellos datos requeridos para reconstruir la interfaz de usuario en un punto de tiempo determinado.

En términos simples, el estado es el conjunto de todos aquellos datos que necesitamos considerar para decirle a nuestra aplicación qué es lo que se va a renderizar al momento de mostrar la interfaz a los usuarios.

Cabe considerar que desde un punto de vista mucho más general, el estado incluye todos aquellos recursos que nuestra aplicación utiliza. Es decir, todas aquellas imágenes, audios, fuentes, e incluso animaciones que utilizamos para nuestra aplicación. Sin embargo, muchos de estos recursos son directamente manejados por el framework y no requieren de nuestras implementaciones para funcionar. Es por este motivo que es necesario estudiar aquél estado que sí debemos gestionar directamente para que la aplicación funcione de acuerdo a lo que necesitamos.

Para ello, haremos una revisión de los tipos de estado con los que nos podemos encontrar al momento de trabajar en nuestra aplicación:

Estado efímero

Cuando hablamos de estado efímero, hacemos directa referencia a todo aquel estado que se encuentra definido dentro un widget en específico y que no requiere ser accedido por uno externo en el árbol de widgets de la aplicación.

Para poder utilizar este estado, requerimos un StatefulWidget, que a través del método setState(), nos posibilita modificar el dato que se requiere para volver a construir el widget y actualizar la interfaz de usuario de acuerdo a lo requerido bajo el contexto de la aplicación.

En términos resumidos, cada vez que necesitamos hacer que un widget se vuelva a construir en respuesta a entradas de usuario y no requerimos que el estado de ese widget sea utilizado por otros, utilizar el estado efímero mediante un  StatefulWidget es la opción más adecuada.

Estado de la aplicación

En una aplicación sencilla, el estado efímero puede llegar a ser la solución óptima. Sin embargo, a medida que la aplicación crece y la necesidad de compartir el estado entre varias vistas aumenta, utilizar únicamente el estado efímero deja de ser suficiente y complica un fácil mantenimiento. Por lo tanto,  todo aquel estado que queremos compartir entre varios widgets, e incluso almacenar entre varias sesiones de usuario es aquel que llamamos estado de la aplicación.

Es precisamente este tipo de estado el que estaremos discutiendo en este articulo. A medida que avancemos, nos daremos cuenta que la cantidad de opciones disponibles para administrar el estado correctamente varía ampliamente. No obstante, es de acuerdo a varios factores que nos permitiremos decidir cuál es el más adecuado para nuestro proyecto.

————————

Con el objetivo de facilitarnos la identificación de cuál tipo de estado debemos utilizar para nuestra aplicación, la documentación oficial de Flutter nos ofrece esta simple pero poderosa gráfica:

Es de alta importancia resaltar que nuestras aplicaciones pueden contar con estos dos tipos de estado al mismo tiempo. Es decir, el estado efímero y el de la aplicación no son mutuamente excluyentes e identificar en qué widgets de nuestra aplicación podemos utilizar cada tipo nos posibilitará crear un código mucho más limpio y escalable.

Differentiate between ephemeral state and app state | Flutter

Flutter es declarativo

Uno de los enfoques de programación para la construcción de interfaces más comunes durante las últimas décadas ha sido el imperativo.  Este estilo posibilita la construcción de interfaces con un enfoque en el cómo se va a construir la interfaz. Sin embargo, Flutter al ser declarativo te permite concentrarte en el qué se va a mostrar en la interfaz.

  • Enfoque Imperativo: Posibilita especificar el procedimiento que se seguirá para que la interfaz de usuario se vea renderizada y reconstruida.
  • Enfoque declarativo: Permite especificar qué componentes de la vista se deben crear y reconstruir sin necesidad de especificar los pasos a seguir para lograr este objetivo.

Adicionalmente, Flutter no modifica instancias de widgets de la interfaz como en otras tecnologías,. En vez de esto,  Flutter directamente crea una nueva instancia de widget cuando la UI es modificada debido a la rapidez con la que esta herramienta puede lograrlo.

Considerando que Flutter es declarativo, este framework construye la interfaz de la aplicación considerando el estado. En otras palabras, toda la interfaz se construye respecto al estado que se tiene en el momento de la construcción de la interfaz, y cuando este cambia, la interfaz se reconstruye considerando el nuevo estado.

Fórmula obtenida directamente de la documentación oficial de Flutter Start thinking declaratively | Flutter

Con esta simple fórmula podemos concluir que la Interfaz de nuestra aplicación es el resultado del método build de nuestros widgets en función del estado de nuestra aplicación al momento de la construcción.

Es importante mencionar que el estado puede cambiar debido a multiples factores, entre estos podemos identificar las entradas del usuario utilizando el aplicativo, respuestas a llamados que hacemos a una API e incluso hilos internos en los que realizamos cálculos para cumplir con los casos de uso de la aplicación. Por lo que, al ser un framework declarativo, especificar directamente el estado del que un widget depende para su construcción es de alta importancia.

¿Cuáles son mis opciones para gestionar el estado?

Como mencionamos anteriormente, la variedad de opciones para gestión de estados es bastante amplia. La comunidad de Flutter ofrece continuamente más opciones y este hecho en conjunto a que las necesidades de tu aplicación varian ampliamente puede resultar abrumador. Con todo, trataremos de listar aquellas opciones que seguramente oirás regularmente al momento de discutir respecto a la Gestión de estado en Flutter:

Bloc

Bloc es uno de los patrones y librería de gestión de estado más populares de Flutter. Bloc significa Business logic component, es decir, componente de lógica del negocio. Este permite construir aplicaciones robustas a través de unas serie de eventos que permiten procesar la información recibida al momento de detectar el evento con el objetivo de crear un nuevo estado.

A pesar de que podemos encontrar múltiples librerías que implementan este patrón para la gestión de estado de nuestras aplicaciones, el flujo que sigue el mismo puede verse ejemplificado de la siguiente forma:

  1. El usuario presiona un botón de la aplicación y activa un evento.
  2. Bloces informado respecto a la activación de este evento y solicita datos a un repositorio encargado de proveerla (mediante un llamado a una base de datos o una API).
  3. Blocrecibe los datos solicitados y de acuerdo a ellos, emite un nuevo estado con la información requerida por el widget que lo está escuchando.
  4. El widget escuchando a Bloc para recibir los posibles estados que puedan ser enviados a él, recibe el estadoy reconstruye el widget de acuerdo al nuevo estado.

Bloc es una librería ampliamente utilizada para aplicativos de alta escalabilidad debido a cómo su implementación se puede adaptar a todo tipo de modelos de dominio. Sin embargo, es una librería que puede resultar compleja para desarrolladores recién iniciados con el framework debido a su significativa curva de aprendizaje.

Librería recomendada: flutter_bloc | Flutter Package

Provider

Provider es otra de las más famosas librerías de la comunidad. Fue una de las primeras recomendadas por Flutter para el manejo del estado de las aplicaciones de los desarrolladores.

Con Provider, se establecen widgets que notifican cambios de estado en un punto del árbol de widgets, mientras que otros escuchan esos cambios para saber cuándo se debe hacer una reconstrucción de la interfaz. Es decir, tan pronto como se detectan cambios en el estado de la aplicación, los widgets que requieran de esos datos para la renderización de la vista al usuario, se verán reconstruidos sin necesidad de afectar innecesariamente otros componentes en el árbol de Widgets.

En Provider, contamos con 3 Widgets que hacen lo anterior posible:

  • ChangeNotifier: Este componente permite notificar a los widgets que se encuentran escuchándolo cuando cambios de estado ocurren.
  • ChangeNotifierProvider: Este componente permite escuchar y exponer los componentes ChangeNotifier. Esta clase es básicamente un wrapper provider para aquellos widgets que implementan ChangeNotifier.
  • Consumer: Este Widget es aquél que escucha los cambios notificados por el ChangeNotifier y se encarga de correr el método build para aplicar los cambios correspondientes al nuevo estado de la aplicación.

A pesar de ser un gestor de estado funcional para aplicaciones sencillas, Remi Rousselet, creador de esta librería, creó una iteración que no dependiera directamente de Flutter, trabajando en una versión directamente asociada con Dart.

Librería recomendada: provider | Flutter Package

Riverpod

Riverpod es una librería desarrollada intencionalmente por Remi Rousselet para ser una sucesora de Provider. Esta no depende directamente de Flutter, a diferencia de Provider.

Riverpod permite envolver la aplicación en un ProviderScope , que posibilita llamar y tener acceso al Provider requerido desde cualquier Widget de la aplicación en el que desees  utilizarlo, lo que lo hace una implementación de estado directa, sencilla y rápida.

Una de las mayores ventajas de Riverpod respecto a Provider, es que al no depender de Widgets, es posible declarar un provider y utilizarlo en cualquier widget de la aplicación independientemente del widget padre. Los Providers con Riverpod son declarados como variables globales que se colocan en cualquier archivo del proyecto.

Librería recomendada: flutter_riverpod | Flutter Package

————————

Existen muchas otras opciones para discutir respecto a gestión de estado como InheritedWidget & InheritedModel,  Redux,  Getit, Mobx, Binder, GetX, states_rebuilder y más. Cada una de estas opciones cuenta con sus propias implementaciones, ventajas y desventajas.

Sin embargo, es importante identificar cuál de estas opciones conviene más para tu proyecto y equipo de trabajo:

¿Por qué es importante identificar el gestor de estado más adecuado para mi proyecto?

Es de alta importancia resaltar que en el mundo del software los factores que pueden definir las soluciones más adecuadas para los requerimientos de nuestro proyecto varían de acuerdo a varios factores.

De acuerdo a lo que mencionamos anteriormente, aquellas aplicaciones sencillas en las que no requerimos manejar un estado mayor al que utilizaremos en un único widget, un StatefulWidget puede bastar para alcanzar los requerimientos mínimos. No obstante, una aplicación que cuenta con varias vistas en las que compartiremos bastantes datos entre ellas,  compartir información entre varios usuarios y en la que se planea escalar después de finalizar la version con el producto mínimo viable, el utilizar StatefulWidgets puede no ser suficiente.

En adición a lo anterior,  cada uno de los gestores de estados disponibles ofrece enfoques diferentes para el mismo objetivo. Implementar Bloc o incluso Provider en una aplicación sencilla puede resultar exagerado, y a pesar de ser posible, existen soluciones que requieren mucho menos tiempo para cumplir el mismo objetivo. Sin embargo, todo esto está sujeto a los criterios que le permitirán a tu equipo decidir qué opción le conviene más al proyecto.

¿Cuál gestor de estado es el mejor para mis proyectos?

Depende.

Justo como mencionamos previamente, es imposible apuntar a una bala de plata que ofrezca la mejor solución para todos los proyectos de ingeniería de software que trabajemos con Flutter. Los siguientes criterios nos permiten tener una mejor idea de cuál herramienta para gestionar el estado nos brinda las mejores condiciones para nuestros proyectos:

  • Escalabilidad del proyecto: Identificar la proyección y planeación del proyecto a mediano y largo plazo nos permitirá saber la importancia de encontrar un gestor de estado que escale fácilmente.
  • Experticia del equipo del desarrollo del proyecto: A pesar de que un desarrollador siempre debe estar dispuesto aprender, considerar el dominio del equipo respecto a los gestores de estado puede implementar soluciones mucho más rápidas y robustas de acuerdo a la opción escogida.
  • Actividad del repositorio del gestor de estado: Escoger un gestor de estado que es constantemente actualizado evidencia que los mantenedores continuarán arreglando potenciales bugs y ofreciendo soluciones aún más sencillas a implementaciones actuales.
  • Documentación: Una librería bien documentada permite una implementación llevadera y segura, lo que reduce el espacio a errores y problemas de rendimiento.
  • Comunidad de desarrolladores: Tener una comunidad dispuesta a resolver dudas, que a su vez contribuye a una documentación mucho más compleja, facilita la implementación y mantenimiento del gestor de estado escogido.

 

Es importante tener en mente que a pesar de que aquí ofrecemos una lista de aspectos y/o factores guía, te invitamos a identificar y listar más con tu equipo de trabajo teniendo en cuenta los alcances de la organización, Estos factores pueden ser adaptados y/o modificados de acuerdo a las necesidades de la empresa y el proyecto.

Adicionalmente, recomendamos fuertemente no limitarse a conocer únicamente uno o dos gestores de estado, leer respecto a otras opciones cuando cuentes con la oportunidad te permitirá tomar una decisión mucho más sólida para ofrecer lo mejor a tu equipo de desarrollo y a los stakeholders del proyecto!

————————

Agradecemos mucho por el tiempo dedicado a la lectura de este artículo. Esperamos que puedas utilizar el conocimiento aquí expuesto para trabajar en la mejor solución posible respecto a los gestores de estado para los proyectos Flutter con tu equipo de trabajo.

Más artículos