DEV Community

Cover image for 【Flutter】Mastering Animation ~2. multiple effects and TweenSequence~
heyhey1028💙🔥
heyhey1028💙🔥

Posted on

【Flutter】Mastering Animation ~2. multiple effects and TweenSequence~

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?

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

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,
        ),
      ),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

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.

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.

tweenSequnece = TweenSequence<T>([
    TweenSequenceItem(tween: Tween(),weight:1),
    TweenSequenceITem(tween: Tween(),weight:1),
    TweenSequenceITem(tween: Tween(),weight:1),
]);
Enter fullscreen mode Exit fullscreen mode

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.

animation = controller.drive(TweenSequence([]))
Enter fullscreen mode Exit fullscreen mode

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.

TweenSequenceItem(
    tween: Tween(
    begin: const Alignment(-1, 3),
    end: Alignment.topLeft,
    ),
    weight: 2,
)
Enter fullscreen mode Exit fullscreen mode

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

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,
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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

Latest comments (0)