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)