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


Top comments (0)