Animaciones en Flutter: Primeros pasos

- Andrés Cruz

Animaciones en Flutter: Primeros pasos

Es bien sabido que Flutter ofrece una forma muy sencilla de crear hermosas aplicaciones multiplataforma (Android e iOS, y Escritorio y Web en el canal beta actualmente). Desde la perspectiva de un desarrollador de Android e IOS (como yo), esto puede ser cierto en muchos niveles, ya que puede crear su interfaz de usuario con código declarativo y funcional, utilizando conceptos sencillos como Columna o Fila, en lugar de sumergirse en archivos XML (que no son código como tal), o incluso el famoso storyboards que es a nivel de Interfaz Gráfica; que en conjunto todo esto es un pequeño infierno, aumenta la complejidad de desarrollar aplicaciones para ambas plataformas que sigan una misma línea (por ejemplo, una aplicación de una tienda en línea para un cliente que quiere su app para Android, iOS y por supuesto una web).

Si has trabajado con la API de animación de Android, sabrá que son un dolor de cabeza en muchos niveles: una interfaz compleja, muchas API (algunas de ellas en desuso) y, al final, terminarás sin saber cómo implementar tus animaciones aparte de las librerías de soporte... y sin contar con el proceso de animación en iOS que es un mundo a parte de su competidor más cercano de Android con iOS.
Este no es el caso de las animaciones de Flutter, ¡ya que solo hay una manera de implementarlas! y es común para Android e IOS, ya que recuerda que Flutter no emplea primitivas de iOS por ejemplo, si quieres implementar un botón, no emplea las primitivas de Button en Android y UIButton en iOS, si no lo dibuja TODO desde cero para cada plataforma; lo cual es excelente ya que Flutter tiene control total y gana velocidad.

Animaciones implícitas mediante Wigets

Las animaciones implícitas no son más que widgets que gestionan toda la parte difícil de la animación por ti. En realidad, estos widgets son muy similares a los que quizás ya haya implementado en su código. Estoy hablando de, por ejemplo:

  • AnimatedPadding, la contraparte animada del widget Padding.
  • AnimatedOpacity, contraparte animada del widget Opacity.
  • Entre otras.

Estos dos ejemplos son muy puntuales, y su comportamiento se corresponde mucho con su nombre (¡ESO ES un buen nombre de clase!).

Su estructura base es la siguiente:

Estos widgets solo necesitan algunas cosas para funcionar: 

Un child como casi todo en Flutter, puede ser cualquier cosa (que puede ser cualquier widget), Una duración (expresada en horas, minutos, segundos, milisegundos, etc.) y un valor que debe ser administrado por un StatefulWidget. Este último punto es muy importante, ya que cada vez que se cambia el valor dentro de la función setState (por lo tanto, se reconstruye el widget), la animación se lanzará automáticamente mediante una interpolación del valor antiguo y el nuevo que en pocas palabras significa, tener una suave transición de un estado a otro.

import 'package:flutter/material.dart';

class Animation1 extends StatefulWidget {
  @override
  _Animation1State createState() => _Animation1State();
}

class _Animation1State extends State<Animation1> {
  double opacity = 0.0;
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        AnimatedOpacity(
          child: Image.asset(
            'assets/img.png',
            fit: BoxFit.fill,
          ),
          duration: Duration(seconds: 3),
          opacity: opacity,
        ),
        FlatButton(
          onPressed: _changeOpacity,
          child: Text('Animate'),
        )
      ],
    );
  }

  void _changeOpacity() {
    setState(() {
      opacity = 1.0;
    });
  }
} 

Figura 1. Variar opacidad animada en Flutter

El fragmento de código anterior cambió el valor de la variable opacidad dentro de una función setState, que se tradujo en un desvanecimiento fácil y asombroso en la transición para nuestra imagen. Y, al final, este widget manejó todas las cosas de la animación por sí mismo. Lo único que teníamos que hacer era cambiar el estado de nuestro widget.

Widget AnimatedContainer 

¡Esto es genial, solo puedes usar estos widgets para comenzar a usar animaciones en tu aplicación! Pero hay un problema: las animaciones vistas hasta ahora solo animan una propiedad de nuestro hijo. Eso no es muy flexible y puede causar muchos problemas de escalabilidad si simplemente ajustamos los widgets sin control, lo que obviamente conduce a un código pobre y críptico (sabes que Dart puede ser un infierno de sangría si no se escribe correctamente).

En pocas palabras, estas animaciones las podemos emplear para cosas realmente simples, sin implementar una lógica perse en el momento que queramos arrancar la animación, variar comportamientos, duraciones, revertir o detener la animación, nada de esto y mucho mas NO lo podemos hacer con estos contenedores.

La panacea: AnimatedContainer.

Su comportamiento es exactamente el mismo que el de un contenedor común, pero la mejor parte es que cualquiera de sus propiedades se puede animar. Lo único que tenemos que hacer es decidir qué cambios se le harán a nuestro hijo.

Digamos que queremos cambiar tanto la altura como el ancho de nuestro contenedor, y también su color (sí, los colores se pueden interpolar). Lo primero que debemos hacer es definir los campos de nuestro StatefulWidget:

  var imgHeight = 100.0;
  var imgWidth = 100.0;
  var backgroundColor = Colors.white;

Y ahora, nuestro contenedor:

AnimatedContainer(
  height: imgHeight,
  width: imgWidth,
  color: backgroundColor,
  child: Image.asset('assets/img.png', fit: BoxFit.fill),
  duration: Duration(seconds: 3),
)

Y decida cuáles serán los nuevos valores que se asignan en nuestra función setState:

  void _changeValues() {
    setState(() {
      // new values to interpolate
      imgWidth = 300;
      imgHeight = 300;
      backgroundColor = Colors.deepPurpleAccent;
    });
  } 

Figura 2. Múltiples propiedades sobre una imagen en Flutter.

Como puede ver, se anima más de una propiedad al mismo tiempo utilizando AnimatedContainer.

Este es el código fuente:

Dije que cualquier propiedad se puede animar en este widget. Probemos y animemos su parámetro de decoración siguiendo los pasos anteriores:

import 'package:flutter/material.dart';

class Animation3 extends StatefulWidget {
  @override
  _Animation3State createState() => _Animation3State();
}

class _Animation3State extends State<Animation3> {
  var imgHeight = 100.0;
  var imgWidth = 100.0;
  var backgroundColor = Colors.white;
  var borderRadius = 0.0;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        AnimatedContainer(
          height: imgHeight,
          width: imgWidth,
          decoration: BoxDecoration(
            color: backgroundColor,
            borderRadius: BorderRadius.circular(borderRadius),
          ),
          child: Image.asset('assets/img.png', fit: BoxFit.fill),
          duration: Duration(seconds: 3),
        ),
        FlatButton(
          onPressed: _changeValues,
          child: Text('Animate'),
        )
      ],
    );
  }

  void _changeValues() {
    setState(() {
      // new values to interpolate
      imgWidth = 300;
      imgHeight = 300;
      borderRadius = 150.0;
      backgroundColor = Colors.deepPurple;
    });
  }
} 

Figura 3. Animar más propiedades

Como dije, CUALQUIER propiedad del widget Container se puede animar. :) Finalmente, incluso podemos crear una pequeña coreografía secuencial para nuestro pequeño amigo Ditto. Pero esto implicaría rastrear el estado de los campos de la clase, agregando muchas variables nuevas, muchos condicionales para establecer los nuevos valores, y hacerlo nos llevaría a una gran cantidad de repetición.

Conclusión

Esto es solo para comenzar con las animaciones. Solo quieres que tu aplicación se vea genial sin hacer un gran esfuerzo. La interfaz es simple y fácil de usar. Pero hay más recursos que nos permiten tener más control en nuestras animaciones. Pero ese es un tema para el próximo. :)

Recuerda que este es el artículo original.

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz en Udemy

Acepto recibir anuncios de interes sobre este Blog.