【Flutter】Mastering Animation ~4. The confusing parts of animations~
Tags: flutter, dart, beginners
Published: 2022-11-12 13:41:10

# The confusing part of animations
Adding animations is not always necessary to create an app, but there are still plenty of opportunities to write them.

I have implemented animations quite some time. And still, every time I write, I am baffled by how to implement animations.

In this article, I'll try to sort out some of the areas that personally confuse me.

# 1. there are AnimatedXXX and XXXTransition
If you look for articles on animation, you will often see classes called `AnimatedXXX` and `XXXTransition`. These classes are used to simplify the handling of certain animations.

However, these classes have always baffled me.

For example, if I want a widget to fade in, I can use either `AnimatedOpacity` or `FadeTransition` to achieve a fade-in animation.

So what's the difference? And when should you use which?

## ImplicitlyAnimatedWidget and AnimatedWidget
`AnimatedXXX` and `XXXTransition` classes support a variety of animations other than fade-in animations, and each of them has a corresponding class in both classes.

These two groups of classes inherit from separate parent classes, `ImplicitlyAnimatedWidget` and `AnimatedWidget`.

`ImplicitlyAnimatedWidget` is inherited by the `AnimatedXXX` class group. And `AnimatedWidget` is inherited by the `XXXTransition` class group.

Since "classes that inherits `ImplicitlyAnimatedWidget`" and "classes that inherits `AnimatedWidget`" are too long, we call them **Implicit widgets** and **Transition widgets**, respectively.

(Google's engineers also called them this way, so it should be reasonable.

Please keep in mind that these are not the actual class names.

## How are they different?
Difference between the two groups is simply whether or not they contain an `AnimatedController`.

`Implicit widgets` holds `AnimationController` within, while `Transition widgets` requires `AnimationController` to be passed as an argument.

Let's write a widget that changes scale in both class


**Implicit widgets**
```dart
// define variable for Implicit widget
  double implicitScale = 1;

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      body:
        ...
        // Implicit widget
        AnimatedScale(
          scale: implicitScale, // <<< passing variable
          duration: const Duration(seconds: 1),
          child: const Text("Hi, I'm Implicit"),
        ),
        ...
          // change the variable passed to Implicit widget
          onPressed: () {
            if (implicitScale == 1) {
              setState(() => implicitScale = 3);
              return;
            }
            setState(() => implicitScale = 1);
          },
          ...
```

In `Implicit widgets`, you pass the variable you want to change (scale in the above example) and `duration`.

When you change the value of the variable to other value, the widget will animate the widget from starting value to changed value with defined duration.

It's that simple.

**Transition widgets**
```dart
  // define Animation which will be binded to Transition widget
  late AnimationController explicitController;
  late Tween<double> explicitTween;
  late Animation<double> explicitAnimation;

  @override
  void initState() {
    // create Animation for Transition widget
    controller =
        AnimationController(duration: const Duration(seconds: 1), vsync: this);
    explicitTween = Tween(begin: 1, end: 3);
    explicitAnimation = explicitTween.animate(controller);
    super.initState();
  }

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      body:
        ...
        // Transition widget 
          ScaleTransition(
            scale: explicitAnimation, // <<< bind Animation created
            child: const Text("Hi, I'm Explicit"),
          ),
        ...
          // controller Animation class binded to Transition widget
          onPressed: () {
            if (!controller.isCompleted) {
              controller.forward();
              return;
            }
            controller.reverse();
          },
          ...
```

In `Transition widgets`, `Animation` is created from `AnimationController` and `Tween` just as previous examples. It is passed as an argument. 

Similarly, `AnimationController` is manipulated to fire the animation.

## When to use which?
Now that you know the various classes involved in animation, how do you know which one to use?

It all depends on **how complex you want to make your animation**.

The following diagram, which is from two years ago, was created by an engineer at Google.

<img width="800" alt="Screen Shot 2022-11-05 at 22 35 33" src="https://user-images.githubusercontent.com/44666053/200156821-e9d99f9d-9398-48e1-8be4-d89e1590bc5a.png">

reference：https://www.youtube.com/watch?v=PFKg_woOJmI

The more you go from left to right, the customization freedom and complexity rises.

**Explicit widgets** in the diagram represents a use case where defining your own `AnimationController`, `Tween`, `Curve` and `Animation` and wrap them in an `AnimationBuilder`.

### **Use Implicit widgets**

**1.You want to animate to multiple values**

`Implicit widgets` will do a nice job of animating from the current value to the new value you pass in.

You can change the value as many times as you want, as long as you change the value.

This is more flexible than using a `Tween` where you have to define the end value in advance.

**2. For animations that only forwards**

`Implicit widgets` are simpler because you do not need to manipulate the `AnimationController`. But that means they do not allow for complex operations that can be performed with the `AnimationController`.

Therefore, it is not possible to "forward and reverse" or repeat animation with a single trigger.

Having said that, `Implicit widgets` is more suitable to animation that animates from the start value to the end value only once.


### **Use Transition widgets**

**1.You want to create complex animations using `TweenSequence` or `Interval`**

Transition widgets allow you to define your own `Animation`, so you can freely define a sequential animation using `TweenSequence` or a chain of animations using `Interval`.

You cannot define such complex animations with `Implicit widgets`.

**2.You want to do complex animation operations such as `repeat`**

As mentioned above, `Implicit widgets` can only perform `forward` operation, so you need to use `Transition widgets` if you want to perform `repeat` or `forward and reverse` animation operations.

### **Explicit widgets**
**For more complex animations**
In addition to the above, if you want to add multiple animation effects to a widget, it is better to implement in Explicit ways, preparing `AnimationController`, `Tween`, `Curve` and `Animation` by yourself.
Otherwise you will need to wrap it with multiple `Transition widgets`, which will reduce readability. 

## 2. You can generate Animation with both drive() and animate()
In my article, I have used the `drive` method of `AnimationController` to generate `Animation`, but you can also generate `Animation` by passing `AnimationController` to the `animate` method of `Tween`. 

In other words, you can generate `Animation` from both the `AnimationController` side and the `Tween` side.

```dart
// create with AnimationController.drive
Animation = AnimationController.drive(Tween);

// create with Tween.animate
Animation = Tween.animate(AnimationController);
```

This was very confusing when learning animations for the first time.

### When to use which?
Two methods returns exactly the same result (`Animation`), so honestly, it doesn't matter whichever you use.

When considering, it should focus on **"How many `AnimationController` and `Tween` are needed to achieve the desired animation?"**

For example, the following code applies multiple animation effects to a single widget

```dart
final controller = AnimationController(duration: Duration(seconds: 1),vsync:this);

final alignTween = Tween(begin: Alignment.topCenter,end: Alignment.bottomCenter);

final rotateTween = Tween(begin:0, end: pi *8);

final alignAnimation = alignTween.animate(controller);

final rotateAnimation = rotateTween.animate(controller);

 ...
 AnimationBuilder(
  animation:controller,
  builder: (context, _){
    return Align(
      alignment: alignAnimation.value,
      child: Transform.rotate(
        angle: rotateAnimation.value,
        child: Container(),
      ),
    );
  }
 ),
 ...

```

In such a case, since there are multiple Tweens for one `AnimationController`, 
using `animate` method will make it easier to see from which tween, `Animation` is generated from. 

On the other hand, if you want to apply same animation effect to two separate widgets, I would use the `drive` method of the `AnimationController` which make it easier to see from which `AnimationController`, `Animation` is generated from.

```dart
final controllerA = AnimationController(duration: Duration(seconds: 1),vsync:this);

final controllerB = AnimationController(duration: Duration(milliseconds: 500),vsync:this);

final offsetTween = Tween(begin: Offset(0,1), end:Offset.zero);

final animationA = controllerA.drive(offsetTween);

final animationB = controllerB.drive(offsetTween);

 ...
 AnimationBuilder(
  animation:Listenable.merge([
    animationA, 
    animationB,
  ]),
  builder: (context,_){
    return Column(
      children: [
        Transform.translate(
          offset: animationA.value,
          child: WidgetA(),
        ),
        Transform.translate(
          offset: animationB.value,
          child: WidgetB(),
        )
      ]
    );
  }
 ),
 ...
```

On the other hand, when `AnimationController` and `Tween` are one-to-one, you can use either one.

However, the above is just my personal opinion, so you can decide which one to use according to your preference.

## 3. Similar classes
The existence of similar classes are another reason why animation can be confusing.

For example
- `AnimationBuilder`
- `TweenAnimationBuilder`
- `Curves`
- `Animation`
- `Tween`
- `CurvedAnimation`
- `CurveTween`

As you can see from the names, classes seems to be a combination of one of the main animation players such as `Animation`, `Curve`, `Tween`, etc.

It can easily get confusing understanding the difference and how and when to use which class.

For example, the following codes are written in different ways, but they all achieve the same animation.

Example using `CurvedAnimation`

```dart
final controller = AnimationController(duration: Duration(seconds: 1),vsync:this);

final animation = CurvedAnimation(
  parent: controller,
  curve: const Curve.ease,
);

 ...
 AnimationBuilder(
  animation:animation,
  builder:(context,_){
    return Opacity(
      opacity: animation.value,
      child: Container(),
    );
  } 
 ),
 ...
```

Example using `CurveTween`

```dart
final controller = AnimationController(duration: Duration(seconds: 1),vsync:this);

final animation = controller.drive(CurveTween(curve:Curves.ease));

 ...
 AnimationBuilder(
  animation:animation,
  builder:(context,_){
    return Opacity(
        opacity: animation.value,
        child: Container(),
    );
  } 
 ),
 ...
```

Example using `TweenAnimationBuilder`

```dart

 ...
 TweenAnimationBuilder<double>(
  tween: CurveTween(curve: Curve.ease),
  duration: Duration(seconds: 1),
  builder:(context,opacity,_){
    return Opacity(
      opacity: opacity,
      child: Container(),
    );
  } 
 ),
 ...
```

You would also find some examples which passes `AnimationController` directly, instead of passing `Animation`.

```dart
final controller = AnimationController(duration: Duration(seconds: 1),vsync:this);

 ...
 AnimationBuilder(
  animation:controller,
  builder: (context,_){
    return Opacity(
      opacity: controller.value,
      child: Container(),
    );
  }
 ),
 ...
```

Looking at all these different ways of writing animations, it is easy to get confused about how the classes are related to each other.

In such a case, it is recommended to classify each class by what parent class it inherits from.

![image](https://user-images.githubusercontent.com/44666053/200590332-61bb61de-6701-44f5-8c6f-ea3cdee5b3bf.png)


By looking at the diagram, you would understand that `AnimationController` sometimes is passed directly as an animation because it inherits `Animation`. Also, you might understand why `CurveTween` can be passed as a `tween`, and why `TweenAnimationBuilder` doesn't need to be passed when using `AnimationController`. 

Also, since `AnimationController` has a value that changes from 0 to 1 by default, it can be used as an `Animation` of `Tween(begin:0,end:1)`, so you can pass it as an `animation` parameter without having to tie a `tween` to it. can be used as `Animation` of `(begin:0,end:1)`.

### Sample Repository
https://github.com/heyhey1028/flutter_samples/tree/main/samples/master_animation

## That's it!!
Because there are so many different classes, Animations can be overwhelming at first. But if you understanding the things described in this article, I think you will get much familiar with animations.

I hope these series of articles will help you understand animation a little better.

*Happy dev life with Animation!!!!*

### Reference
- https://www.youtube.com/watch?v=PFKg_woOJmI (en)
- https://www.youtube.com/playlist?list=PLjxrf2q8roU2v6UqYlt_KPaXlnjbYySua (en)