Animación en Flutter (1)
Tags: flutter
Published: 2019-10-21 21:16:50

###Introducción###
Existen animaciones que vienen "de serie" en los Widgets suministrados por las APIs de Flutter pero es frecuente que necesitemos alguna animación específica por requerimientos, por usabilidad o por dar un poco de "brillo" a nuestra aplicación. 

Este artículo pretende mostrarte el abc de la animación en Flutter junto con un ejemplo lo más simple y claro posible para que tengas una base desde donde encarar animaciones más complejas.

##Arquitectura###
En una animación Flutter necesitamos tres instancias de:

**Clase abstracta Animation** 
1. En el objeto [Animation](https://api.flutter.dev/flutter/animation/Animation-class.html), definimos los valores de inicio y de fin de la animación y contiene el valor actual durante el transcurso de la misma. El valor de la animación no tiene porque ser numérico como veremos en breve.
2. Contiene los importantes métodos [addListener](https://api.flutter.dev/flutter/animation/ReverseAnimation/addListener.html) y [addStatusListener](https://api.flutter.dev/flutter/animation/Animation/addStatusListener.html) para añadir listeners que informarán a la aplicación de los cambios en la animación.
3. La clase Animation es abstracta y por tanto no es obtenida directamente sino a través de las clases [Tween](https://api.flutter.dev/flutter/animation/Tween-class.html), [ColorTween](https://api.flutter.dev/flutter/animation/ColorTween-class.html), [TextStyleTween](https://api.flutter.dev/flutter/widgets/TextStyleTween-class.html), [AlignmentTwen](https://api.flutter.dev/flutter/rendering/AlignmentTween-class.html), etc...dependiendo de lo que queramos animar. En el ejemplo que suministro al final he elegido la clase Tween porque quiero que el valor sea un numérico, pero si hubiera querido animar por ejemplo un color podría haber elegido la clase ColorTween.

**Clase AnimationController**
1. La instancia de [AnimationController](https://api.flutter.dev/flutter/animation/AnimationController-class.html) es un parámetro para la construcción del objeto Animation pero realmente es AnimationController quien realiza el trabajo importante de la animación. En base a la duración de la misma, va interpolando los valores apropiados entre su inicio-fin y llama en el momento justo al listener de nuestra aplicación.
2. Contiene los métodos [forward](https://api.flutter.dev/flutter/animation/AnimationController/forward.html), [repeat](https://api.flutter.dev/flutter/animation/AnimationController/repeat.html), [reset](https://api.flutter.dev/flutter/animation/AnimationController/reset.html), [reverse](https://api.flutter.dev/flutter/animation/AnimationController/reverse.html), etc...para el manejo de nuestra animación.

**Clase abstracta TickerProvider**

1.- AnimationController en su requerido parametro contructor **vsync** usa un [TicketProvider](https://api.flutter.dev/flutter/scheduler/TickerProvider-class.html) que se encarga de notificar cuando se requiere pintar un frame.
2.- No usaremos directamente la clase abstracta TicketProvider sino que generalmente utilizaremos un mixin derivado del tipo [SingleTickerProviderStateMixin](https://api.flutter.dev/flutter/widgets/SingleTickerProviderStateMixin-mixin.html) o un TicketProvider customizado para obtenerlo 

**Ejemplo**

El siguiente ejemplo muestra en pantalla un texto con un valor numérico que va decreciendo a lo largo del tiempo.

Una animación puede ser más o menos complicada pero no se diferenciará en lo esencial de este ejemplo. Atención especial en como usar el primer listener en donde cambiamos el valor del texto y el segundo listener en donde detectamos cuando ha finalizafo la animación.

```dart
import 'package:flutter/material.dart';

void main() {
  runApp(Counter());
}

class Counter extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return CounterState();
  }
}

class CounterState extends State<Counter> with SingleTickerProviderStateMixin {
  Animation<double> animation;
  AnimationController controller;
  String textCounter = "5";

  @override
  Widget build(BuildContext context) {
    return Center(
        child: Container(
            child: Directionality(
                textDirection: TextDirection.ltr, child: Text(textCounter,textScaleFactor: 3))));
  }

  @override
  @mustCallSuper
  void initState() {
    super.initState();

    //Creamos el controller para una animación con una duración de 5 segundos
    controller = AnimationController(
      duration: const Duration(seconds: 5),
      vsync: this,
    );

    /*La clase Animation es abstracta. Sus instancias son obtenidas a través de
    la clase Tween, IntTween, AlignementTween, BorderTween...Tenemos una pléyade
    de clases disponibles dependiento de lo que queremos animar y por tanto de
    lo que querramos interpolar entre frame y frame*/

    animation = Tween<double>(begin: 0, end: 5).animate(controller);

    //Añadimos un listener en este caso para cambiar el texto a 'FINALIZADO!!'
    animation.addStatusListener((AnimationStatus status) {
      setState(() {
        if (status == AnimationStatus.completed) {
          textCounter = "FINALIZADO!!!";
        }
      });
    });

    //Añadimos un listener que es llamado cuando el valor entre el rango
    // begin-end ha cambiado y un frame está preparado para pintar. Es el
    //momento para cambiar el texto con el valor interpolado
    animation.addListener(() {
      setState(() {
        textCounter = this.animation.value.round().toString();
      });
    });

    //Iniciamos la animación
    controller.forward();
  }
}


```
Se pretende hacer un widget que anime un simple Texto para que cuente hasta
 5 y cuando termine ponga el mensaje FINALIZADO!!!

Los comentarios en el código son muy descriptivos. Los hechos relevantes en el código son la obtención de una instancia Animation mediante el método animate de la clase Tween. Ya fijamos que los valores begin y end van a ser 0 y 5.

```
animation = Tween<double>(begin: 0, end: 5).animate(controller);
```

Observa como el parámetro vsync del controller es el propio objeto puesto que estamos usando el mixin en la propia clase CounterState. 

Otro punto crucial del código es el añadir a la animación los callbacks, los listeners de nuestra aplicación. Uno recibirá el valor de la animación durante el tiempo de vida de esta y cambiara el texto. El segundo listener recibirá los diferentes estados por los que va pasando la animación. Cuando este finalizada mostraremos un texto acorde.

```dart
 animation.addStatusListener((AnimationStatus status) {
      setState(() {
        if (status == AnimationStatus.completed) {
          textCounter = "FINALIZADO!!!";
        }
      });
    });

 animation.addListener(() {
      setState(() {
        textCounter = this.animation.value.round().toString();
      });
    });
```