Adding a curve to animations in Flutter

Chaining transformation matrices

Danielle H
4 min readJan 12, 2024

In a previous article, I showed how to do a slide-in animation. Each widget slid into place from below (or the side) using a Transform Widget in an AnimatedBuilder.

But what if you want to add a curve to the slide?

And that is what chaining transformation matrices is for.

If you have knowledge of transformation matrices already, you don’t need this article. Go read a good book with a cup of your beverage of choice next to you :)

Enjoy. [Created using Dall-E]

If not, you’ve come to the right place.

We will build on the code from the previous article. In our AnimatedTile, we used the Transform widget with Matrix4.translationValues. It allows us to specify the translation — a.k.a. movement — we want to move the widget x, y, and z.

// Wraps the child in a Transform with the given slide and
// an AnimatedBuilder with the given animation.
class AnimatedTile extends StatelessWidget {
const AnimatedTile({
super.key,
required this.animation,
required this.slide,
required this.child,
});

final Animation<double> animation;
final int slide;
final Widget child;

@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
child: child,
builder: (context, child) {
return Transform(
transform: Matrix4.translationValues(
0, (1.0 - animation.value) * slide, 0),
child: Padding(padding: const EdgeInsets.all(8.0),
child: child),
);
});
}
}

The Matrix4 class has many other helper functions to create transformations. And the cool thing about this is that you can chain them together by multiplying them. So if you want to add more to your slide-in, all you need to do is multiply the current transformation matrix by another helper function, and you’re done.

Rotation

The rotationX, rotationY and rotationZ functions rotate the widget around the given axis.

X-Axis Rotation:

The widget flips vertically, like a cat flap. Chained together with our slide in, it looks like this:

And the code is:

@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
child: child,
builder: (context, child) {
return Transform(
transform: Matrix4.translationValues(
0, -(animation.value - 1) * slide, 0) *//this is the slide
Matrix4.rotationX((1 - animation.value) * 1),//rotation X
child: child);
});
}

Here, our rotation goes between 1 radian to 0 radians.

Y-Axis Rotation:

This gives a horizontal flip, as if the widget is a door swinging shut.

And the code is:

@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
child: child,
builder: (context, child) {
return Transform(
transform: Matrix4.translationValues(
0, -(animation.value - 1) * slide, 0) *//this is the slide
Matrix4.rotationY((1 - animation.value) * 1),//rotation Y
child: child);
});
}

Z-Axis Rotation:

It’s like the widget is spinning on a record player.

And the code is:

@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
child: child,
builder: (context, child) {
return Transform(
transform: Matrix4.translationValues(
0, -(animation.value - 1) * slide, 0) *//this is the slide
Matrix4.rotationZ((1 - animation.value) * 0.1),//rotation Z
child: child);
});
}

Here, I used a smaller rotation, as 1 radian around the Z axis was too much.

Scaling

We can also add a scaling animation, giving the effect of growth, by multiplying with an additional helper function: Matrix4.diagonal3Values(). Here you specify the scaling in x, y, and z. So for a “growing” animation the values need to be the same on the x and y axes, and 0 in z. Chained with our slide-in and the rotation around the z-axis, it looks like this:

With the following code:

@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
child: child,
builder: (context, child) {
return Transform(
transform: Matrix4.translationValues(
0, -(animation.value - 1) * slide, 0) *//slide
Matrix4.rotationZ((1 - animation.value) * 0.1) *//rotate
Matrix4.diagonal3Values(0.5 + animation.value * 0.5,
0.5 + animation.value * 0.5, 1),//scale
child: child);
});
}

Note the values in the function:

x and y are both 0.5 + animation.value * 0.5, that is, they go between 0.5 and 1. z is 1, signifying no change.

As you can see, you can play around to your hearts’ content, using an AnimatedBuilder, a Transform widget, and the endless possibilities of the Matrix4 helper functions.

Animate on.

98 days and still counting. #BringThemHome.

Check out my free and open source online game Space Short. If you like my stories and site, you can also buy me a coffee.

--

--

Danielle H

I started programming in LabVIEW and Matlab, and quickly expanded to include Android, Swift, Flutter, Web(PHP, HTML, Javascript), Arduino and Processing.