Skip to content

Instantly share code, notes, and snippets.

@marcossevilla
Created January 25, 2021 20:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcossevilla/0470cdbadf8f70756697f24a7c35b284 to your computer and use it in GitHub Desktop.
Save marcossevilla/0470cdbadf8f70756697f24a7c35b284 to your computer and use it in GitHub Desktop.

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

How to generate even more code with Freezed

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...

https://gist.github.com/4a92697c5187871dc62f433071004aa7

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

https://gist.github.com/3ed8e6ee7862af2fdafe7cde57cdd44b

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...

https://gist.github.com/070170ccc310cde7a978011a43440b94

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.

https://gist.github.com/04806ba041bfb6c03f2bbd2d2f0e154b

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.

https://media.giphy.com/media/wkfU652r5hLAxZL8LG/giphy.gif

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:

https://gist.github.com/d165c1d6f3853892e56789023ab71fb3

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:

https://gist.github.com/4f20a3f91bc86a5b7b8a467bc9106409

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:

https://gist.github.com/bf4b747f496b0e66b8b21a151aaa10d5

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

https://media.giphy.com/media/pHb82xtBPfqEg/giphy.gif

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í:

https://gist.github.com/a5310570314dd2770fbe46657bae71b7

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:

https://gist.github.com/b1af5bac49eff82e23be965ff37d7b72

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.

https://media.giphy.com/media/xUPOqo6E1XvWXwlCyQ/giphy.gif

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:

GitHub, dev.to, Medium, LinkedIn, Twitter, YouTube.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment