【Flutter】Mastering Animation ~2. multiple effects and TweenSequence~
Tags: flutter, beginners, dart
Published: 2022-11-12 13:40:33

This article is the second in a series of articles on animation.

In vol. 1, we've gone through on some of the basics of animation.

For next step, let us look into more complex animations.

Complex animations can include the following

- Multiple animation effects on a single widget at the same time
- Sequenced animation on a single widget
- A single widget with different animations at different times
- Staggered animation for multiple widgets
- Animate multiple Widgets separately

Let's look at them one by one

# Applying multiple animations on a single widget
In previous article, I have applied only an alignment animation on a single widget, but what if I want to change the alignment while rotating at the same time?

<img width="350" src="https://user-images.githubusercontent.com/44666053/201475680-d7df3d2e-1506-4d57-b550-05278a4260c9.gif">


Conclusion: Generate multiple animations from a single AnimationController, and bind them to a Widget

I know I'm jumping to conclusions, but this use case is simple!

Prepare a `Tween` for each animation effect (position, rotation, color, size, etc.) you want to add, and generate an `Animation` using that `Tween` and the `AnimationController`.

In the following example, a widget has animating position (alignment) and rotation (rotation).

### Sample code

```dart
class _MultipleEffectState extends State<MultipleEffect>
    with SingleTickerProviderStateMixin {
  late AnimationController controller;
  late Tween<Alignment> alignmentTween; // <<< Tween for first animation
  late Tween<double> rotateTween; // <<< Tween for second animation
  late Animation<Alignment> alignmentAnimation; // <<< first animation
  late Animation<double> rotateAnimation; // <<< second animation

  @override
  void initState() {
    controller =
        AnimationController(duration: const Duration(seconds: 3), vsync: this);
    alignmentTween = Tween(
        begin: Alignment.topCenter,
        end: Alignment
            .bottomCenter); // <<< define start and end value of alignment animation
    rotateTween = Tween(
        begin: 0,
        end: 8 * pi); // <<< define start and end value of rotation animation
    alignmentAnimation =
        controller.drive(alignmentTween); // <<< create align animation
    rotateAnimation =
        controller.drive(rotateTween); // <<< create rotation animation
    super.initState();
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.orange[300],
        title: const Text('Multiple Effect'),
      ),
      drawer: const MainDrawer(),
      body: AnimatedBuilder(
        animation: controller,
        builder: (context, _) {
          return Align(
            alignment: alignmentAnimation.value, // <<< bind align animation
            child: Transform.rotate(
              angle: rotateAnimation.value, // <<< bind rotation animation
              child: const Text('Hello world!'),
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.forward();
        },
        backgroundColor: Colors.yellow[700],
        child: const Icon(
          Icons.bolt,
          color: Colors.black,
        ),
      ),
    );
  }
}

```

# Sequenced animation on a single widget
In the following use case, the same animation effect is applied to a Widget multiple times while changing its position

For example, if we want a Widget to move in the shape of a rectangle, we need to apply the animation to change position multiple times, first to the right, then to the bottom, then to the left, then to the top, and so on.

<img width="350" src="https://user-images.githubusercontent.com/44666053/201475409-6a75b214-4e8f-4d7b-9d72-4438a0dd4da6.gif">



We can use `TweenSequence` class in such cases

## TweenSequece class
`TweenSequence` is a `Tween` that defines a sequence of changes.

`TweenSequence` allows you to specify multiple changes by passing an array of `TweenSequenceItem`, a class that defines how long and what kind of changes you want to make.

```dart
tweenSequnece = TweenSequence<T>([
    TweenSequenceItem(tween: Tween(),weight:1),
    TweenSequenceITem(tween: Tween(),weight:1),
    TweenSequenceITem(tween: Tween(),weight:1),
]);
```

`TweenSequence` inherits `Animatable` class, which is the same super class as `Tween`, meaning it can be handled in the same way as `Tween`.

So you can generate an `Animation` using the defined `TweenSequence` and `AnimationController`. By binding this Animation to a widget, you can make a widget have an animation that changes multiple times.

```dart
animation = controller.drive(TweenSequence([]))
```

## TweenSequenceItem class

`TweenSequenceItem` is a class that can be passed to `TweenSequences`. Each classes are defined with what kind of change you want to `tween` parameter and how long to `weight` parameter.

```dart
TweenSequenceItem(
    tween: Tween(
    begin: const Alignment(-1, 3),
    end: Alignment.topLeft,
    ),
    weight: 2,
)
```

`weight` is a ratio of time. It determines how much of the time (`Duration`) defined in the `AnimationController` will be allocated for that specific `tween`.

Like `flex` in the `Flexible` class, the `Duration` divided by the total of the `weight` is the amount of time that each `TweenSequenceItem` has.

### Sample code
```dart
class _SequenceAnimationState extends State<SequenceAnimation>
    with SingleTickerProviderStateMixin {
  late AnimationController controller;
  late TweenSequence<Alignment>
      tweenSequence; // <<< define as TweenSequence, not Tween
  late Animation<Alignment> animation;

  @override
  void initState() {
    controller =
        AnimationController(duration: const Duration(seconds: 4), vsync: this);
    tweenSequence = TweenSequence<Alignment>([
      // <<< can take in multiple TweenSequenceItem classes
      TweenSequenceItem(
          tween: Tween(begin: Alignment.topLeft, end: Alignment.topRight),
          weight:
              1 // <<< in this example, duration is 4 seconds, so weight:1 means this Tween will be applied for 1 second
          ),
      TweenSequenceItem(
          tween: Tween(begin: Alignment.topRight, end: Alignment.bottomRight),
          weight: 1),
      TweenSequenceItem(
          tween: Tween(begin: Alignment.bottomRight, end: Alignment.bottomLeft),
          weight: 1),
      TweenSequenceItem(
          tween: Tween(begin: Alignment.bottomLeft, end: Alignment.topLeft),
          weight: 1),
    ]);
    animation = controller
        .drive(tweenSequence); // <<< create Animation just like using a Tween
    super.initState();
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.green[300],
        title: const Text('Sequence Animation'),
      ),
      drawer: const MainDrawer(),
      body: AnimatedBuilder(
        animation: controller,
        builder: (context, _) {
          return Align(
              alignment: animation.value, // <<< simply bind the animation
              child: const Text('Hello world!'));
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.forward();
        },
        backgroundColor: Colors.yellow[700],
        child: const Icon(
          Icons.bolt,
          color: Colors.black,
        ),
      ),
    );
  }
}
```

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

# what's next?
The remaining three use cases will be described in the following article

- A single widget with different animations at different times
- Staggered animation for multiple widgets
- Animate multiple Widgets separately

https://dev.to/heyhey1028/flutter-mastering-animation-3-interval-and-multiple-tickerproviders-1okf

### Reference
- https://zenn.dev/inari_sushio/articles/98b1ee55aadd12 (ja)
- https://medium.com/thismightwork/https-medium-com-thomas-cornet-sequenced-animations-in-flutter-7c4fa4117598 (en)