Photo by Alto Crew on Unsplash

Cómo generar aún más código con Freezed

Data classes, uniones y menos código escrito

Marcos Sevilla
Published in
6 min readJan 25, 2021

--

Ya les he hablado sobre todas las bondades y por qué deberían utilizar herramientas de generación de código en sus proyectos. Si no has visto mi artículo sobre FlutterGen, te lo dejo por acá.

Así que no les voy a dar una introducción a generar código, más bien vamos a conocer una herramienta muy buena para generarlo. Esta herramienta se llama Freezed.

Freezed viene a ser un generador de código para clases inmutables y fue creado por Rémi Rousselet, que tal vez lo conozcan más por su paquete Provider.

Inmutabilidad

Clases inmutables -como el nombre lo dice- son clases que no cambian sus propiedades y esta característica es muy buena para el manejo de estado y para usarse en modelos de datos. Aunque no lo noten, ustedes seguramente ya usaron clases inmutables.

Miren este ejemplo…

Aquí podemos ver que accedemos al tema global de nuestra app, específicamente a la propiedad del textTheme o tema del texto. El objeto headline4 contiene una configuración por defecto que no puede cambiarse, es decir, es inmutable.

Ahí es donde entra en acción el copyWith. Este método nos permite devolver una instancia de la misma clase, o un objeto idéntico al que estamos aplicando el método, pero con la posibilidad de cambiarle una o varias propiedades según queramos.

En el ejemplo anterior nos permite cambiar el color del headline4 por medio de copyWith.

Y tal vez en este momento se preguntarán, ¿para qué quiero objetos inmutables si fácilmente puedo asignar otro valor a una propiedad determinada del objeto?

Sí, es cierto. Podés hacer una asignación fácilmente, pero el problema es que se exponen las propiedades y tu código se vuelve “fácil de burlar” le llamaría yo. Esto se usa bastante en manejo de estado porque es mucho mejor cambiar el estado mediante funciones y no tener acceso a las propiedades internas para no cambiarlas fuera de su clase.

Esto incluso nos remonta al principio de Encapsulación de Programación orientada a objetos, donde las propiedades deben ser privadas y acceder o modificarlas por medio de métodos. Sin embargo, aquí no hacemos getters y setters, dejamos acceso a las variables por medio de un estado específico que las contiene. Todo eso sin exponer nuestro componente de lógica.

Lo último que mencioné es muy propio del manejo de estado utilizando StateNotifier, el cual yo utilizo con Riverpod. En un momento van a comprender de lo que hablo, si no han leído mi artículo sobre StateNotifier, se los dejo por acá.

Igualdad de valores

Saliendo un poco de inmutabilidad, Freezed no sólo te ayuda a hacer objetos inmutables, si no a implementar igualdad de valores.

Si ustedes directamente comparan dos instancias iguales (en propiedades), cuando corran el programa no les va a devolver verdadero. Miren…

Corran ese código en DartPad y van a ver que imprime falso, a pesar de que ambos objetos contienen el mismo id y son esencialmente iguales.

Freezed sobre escribe el operador de igualdad (==) y el getter de hashCode, de esa manera, ya podemos comparar dos o más objetos y obtener verdadero. Esto es de mucha utilidad para chequeos en los objetos en capas de más alto nivel.

Solamente tenemos que crear una clase con el decorador @freezed, proveniente del paquete de freezed_annotation, para que Freezed sepa que debe generar código para esta clase.

Una vez hecha esta clase abstracta User, hacemos un mixin con la clase que nos va a generar Freezed. Esta clase generada la llamamos _$User primero porque la hacemos privada con _ para que sólo se pueda acceder a ella desde su mismo archivo y además utilizamos $ porque es una conveniencia indicar este símbolo para que las clases generadas no tengan el mismo nombre de las clases que creamos nosotros.

Finalmente, nuestro constructor factory de User es la clase que ocuparíamos en nuestro código. Podemos crear diferentes constructores como los típicos fromJson y también podemos crear métodos propios. Vamos a ver esto en serialización, mientras tanto generemos nuestro código.

Generando el código

Para generar el código incluimos una dependencia llamada build_runner, este se encarga de correr los generadores de código que haya en el proyecto. Tengan de referencia build_runner para otras veces que ocupen generadores de código, yo no lo recomendé cuando hablamos de FlutterGen porque a veces puede dar problemas de versiones.

⚠️ Dejen todas las dependencias que incluyan sin una versión específica, así se evitan errores de compatibilidad entre las versiones.

En fin, el comando a correr en la raíz del proyecto es el siguiente:

⚠️. Cada vez que hagan un cambio a sus clases van a tener que correr uno de estos comandos para ver los cambios en su código generado, por si ven que hay errores.

Como pequeña nota, si tu proyecto utiliza algún linter como pedantic o very_good_analysis, te sugiero que añadas estas líneas a tu analysis_options.yaml y de esa manera no tengas errores en tus archivos generados:

Serialización

La serialización de datos es parte esencial de los modelos, ya que la mayoría de apps contactan servicios de terceros o APIs hechas a la medida que devuelven respuestas en formato JSON.

Estos JSONs los decodificamos gracias al módulo de dart:convert que ya viene incluido en Dart, pero luego tenemos la necesidad de ubicar cada propiedad del mapa decodificado que nos queda en un modelo de datos.

Ahí es donde entra en acción el constructor fromJson en nuestras clases, que nos permite traducir nuestro mapa a una clase, lo agregamos de esta manera:

Añadimos una nueva directiva part que va a ser del archivo serializable de JSON, y luego agregamos el nuevo factory que vamos a tener para la serialización.

⚠️ Freezed no hace por su propia cuenta esta serialización a JSON, necesita que incluyamos el paquete json_serializable las dev_dependencies del pubspec.yaml.

Puede ir debajo de Freezed.

Ya les presenté muchas de las funcionalidades principales muy potentes que tiene Freezed, ahora vamos a la última que me parece de las más geniales que ahorran muchas líneas de código.

Uniones

Las uniones son una característica que Freezed trae a Dart de otros lenguajes como Kotlin. En Kotlin se les llama clases selladas (sealed-classes). Incluso la sintaxis que Freezed ofrece es muy similar.

Son clases que implementan una determinada clase abstracta e incluyen un “switch” en su clase padre para definir acciones basadas en qué implementación es.

Este último detalle nos reduce bastante el código y yo personalmente lo ocupo para definir los estados de mi componente de lógica. Algo así:

Y luego podemos usar los métodos when o maybeWhen para realizar alguna operación en base a alguno de esos casos, de esta forma:

Uno de los nuevos paquetes más populares de manejo de estado e inyección de dependencias, Riverpod, utiliza Freezed para definir sus providers como clases selladas y así es mucho más sencillo en sintaxis manejar widgets en Flutter que reaccionen a un estado en específico.

Pero como siempre digo, lean la documentación de Freezed, esta es una pequeña introducción para que sepan de qué va, qué pueden hacer y sobre todo, para que pierdan el miedo de explorarlo.

Igualmente les dejo mi repositorio en GitHub donde estuve explorando este paquete. Ya que no he encontrado fácilmente uno que lo incorpore para ver más ejemplos. Es su turno de explorarlo y difundir su uso porque realmente está muy genial.

Lo de siempre…

Si aprendiste algo nuevo y te fue de utilidad, podés compartir este artículo para ayudar a otro/a desarrollador(a) a seguir mejorando su productividad y calidad al escribir aplicaciones con Flutter.

También hay una versión de este mismo artículo en inglés publicado en dev.to. De nada. 🇺🇸

Además, si te gustó este contenido, podés encontrar aún más y seguir en contacto conmigo en mis redes sociales:

Originally published at http://github.com.

--

--