Rotation examples with Flutter Transform (Part I)
In this story, you will find many interesting examples to learn how to rotate a widget using the Transform widget.
Table of Contents
Definition
Rotation around Z-axis (rotation in XY-plane)
• Two alternatives
• Angle of rotation
• Alignment and origin of rotation
• Origin by default
• Before rotating…
• Rotate with origin in the top-left corner
• Rotate with origin in the top-right corner
• Rotate with origin in the center
Rotation around X-axis or Y-axis (3D perspective)
• Visual explanation
• How can we get in Flutter this 3D view of a rotated shape?
• Examples of different values of the angle of rotation
• Pulling the door, pushing the door
• Playing with the perspective parameter
Definition
Transform.rotate creates a widget that transforms its child using a rotation around the center.
Rotation around Z-axis (rotation in XY-plane)
Two alternatives
To rotate around Z-axis (in XY-plane), we have two alternatives:
- Use
Transform
widget withMatrix4.rotationZ(double radians)
as thetransform
property value:
Transform(
transform: Matrix4.rotationZ(0.3),
alignment: Alignment.topLeft,
child: Container(
color: Colors.purpleAccent,
width: 200,
height: 200,
),
)
- Use
Transform.rotate
constructor:
Transform.rotate(
angle: 0.3,
alignment: Alignment.topLeft,
child: Container(
color: Colors.purpleAccent,
width: 200,
height: 200,
),
)
Angle of rotation
The angle gives the rotation in clockwise radians.
If we use the Transform.rotate
constructor, the angle is a property.
Otherwise, in the Transform
widget with Matrix4.rotationZ(double radians)
, the angle is a parameter in Matrix4.rotationZ
.
Alignment and origin of rotation
The alignment controls the origin of the rotation.
According to this, alignment: Alignment.topLeft
would put the origin of the rotation in the top-left corner.
We can set the origin through two properties:
alignment
property, e.g.alignment: Alignment.topLeft
origin
property, e.g.origin: Offset(100,50)
Origin by default
- By default, the origin is the top-left corner WHEN we use the
transform
property withMatrix4.rotationZ(double radians)
value. - By default, the origin is the center WHEN we use the
Transform.rotate
constructor.
Before rotating…
Container( //grey background container (hidden here)
margin: EdgeInsets.only(top: 60, left: 60),
color: Colors.grey,
width: 200,
height: 200,
child: Container(
color: Colors.purpleAccent,
width: 200,
height: 200,
),
)
Rotate with origin in the top-left corner
Using the transform property, the origin is in the top-left corner by default so:
alignment: Alignment.topLeft
=origin: Offset(0,0)
Container( //grey background
margin: EdgeInsets.only(top: 60, left: 60),
color: Colors.grey,
width: 200,
height: 200,
child: Transform(
alignment: Alignment.topLeft, //origin: Offset(0,0)
transform: Matrix4.rotationZ(0.3),
child: Container(
color: Colors.purpleAccent,
width: 200,
height: 200,
),
),
)
Using the Transform.rotate
constructor, the origin is in the center by default so:
alignment: Alignment.topLeft
=origin: Offset(-w/2, -h/2)
that in this case, isorigin: Offset(-100, -100)
Container(
margin: EdgeInsets.only(top: 60, left: 60),
color: Colors.grey,
width: 200,
height: 200,
child: Transform.rotate(
angle: 0.3,
alignment: Alignment.topLeft, //origin: Offset(-100, -100)
child: Container(
color: Colors.purpleAccent,
width: 200,
height: 200,
),
),
)
Rotate with origin in the top-right corner
Using the transform property, the origin is in the top-left corner by default so:
alignment: Alignment.topRight
=origin: Offset(w,0)
that in this case, isorigin: Offset(200, 0)
Container(
margin: EdgeInsets.only(top: 60, left: 60),
color: Colors.grey,
width: 200,
height: 200,
child: Transform(
alignment: Alignment.topRight, //origin: Offset(200, 0)
transform: Matrix4.rotationZ(0.3),
child: Container(
color: Colors.purpleAccent,
width: 200,
height: 200,
),
),
)
Using the Transform.rotate
constructor, the origin is in the center by default so:
alignment: Alignment.topRight
=origin: Offset(w/2, -h/2)
that in this case, isorigin: Offset(100, -100)
Container(
margin: EdgeInsets.only(top: 60, left: 60),
color: Colors.grey,
width: 200,
height: 200,
child: Transform.rotate(
angle: 0.3,
alignment: Alignment.topRight, //origin: Offset(100, -100)
child: Container(
color: Colors.purpleAccent,
width: 200,
height: 200,
),
),
)
Rotate with origin in the center
Using the transform property, the origin is in the top-left corner by default so:
alignment: Alignment.center
=origin: Offset(w/2, h/2)
that in this case, isorigin: Offset(100, 100)
Container(
margin: EdgeInsets.only(top: 60, left: 60),
color: Colors.grey,
width: 200,
height: 200,
child: Transform(
alignment: Alignment.center, //origin: Offset(100, 100)
transform: Matrix4.rotationZ(0.3),
child: Container(
color: Colors.purpleAccent,
width: 200,
height: 200,
),
),
)
Using the Transform.rotate
constructor, the origin is in the center by default so:
alignment: Alignment.center
=origin: Offset(0,0)
that in this case, isorigin: Offset(0,0)
Container(
margin: EdgeInsets.only(top: 60, left: 60),
color: Colors.grey,
width: 200,
height: 200,
child: Transform.rotate(
angle: 0.3,
alignment: Alignment.center, // origin: Offset(0, 0)
child: Container(
color: Colors.purpleAccent,
width: 200,
height: 200,
),
),
)
Rotation around X-axis or Y-axis (3D perspective)
Visual explanation
Consider the next 3D scene in which we will rotate around the X-axis a blue shape in the XY-plane.
This is the shape contained in the XY-plane before being rotated around the X-axis.
And the projection in the XY-plane of the rotated shape is the projection in the XY-plane of the yellow shape. Notice that this is smaller than the original shape (the blue one) in the same plane, XY-plane.
The following is the interesting part: the 3D perspective of a rotated shape. This is how we really see a rotated shape where we see bigger the closest side of the shape and see smaller the furthest one.
How can we get in Flutter this 3D view of a rotated shape?
Starting with the original shape before being rotated:
Container(
margin: const EdgeInsets.only(left: 40, top: 80),
width: 250,
height: 160,
color: Colors.lightBlue,
)
If we rotate it directly by doing transform: Matrix4.rotationX
:
Container(
margin: const EdgeInsets.only(left: 40, top: 80),
width: 250,
height: 160,
color: Colors.lightBlueAccent,
child: Transform(
transform: Matrix4.rotationX(-0.7),
alignment: Alignment.topCenter,
child: Container(
width: 250,
height: 160,
color: Colors.lightGreen,
),
),
)
we get the shape rotates but viewed as its projection in the XY-plane:
So to get the 3D perspective of the rotated shape,
we will use transform: Matrix4.identity()..setEntry(3,2,0.001)
before applying rotation. This operation defines the transformation's identity matrix and set to 0.001 the element at position (3,2) in the matrix. In practice, it creates a 3D perspective because it makes bigger the lengths near us and makes smaller the lengths far from us.
We are rotating -0.7 radians around the X-axis, and we want this axis to match the top side of the container, and we are mean this by this line:alignment: Alignment.topCenter
Container(
margin: const EdgeInsets.only(left: 40, top: 80),
width: 250,
height: 160,
color: Colors.lightBlueAccent,
child: Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateX(-0.7),
alignment: Alignment.topCenter,
child: Container(
width: 250,
height: 160,
color: Colors.amber,
),
),
)
Examples of different values of the angle of rotation
-0.7 radians (-40.10º):
.....
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateX(-0.7),
alignment: Alignment.topCenter,
.....
-1.1 radians (-63.02º):
.....
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateX(-1.1),
alignment: Alignment.topCenter,
.....
-1.4 radians (-80.21º):
.....
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateX(-1.4),
alignment: Alignment.topCenter,
.....
-1.7 radians (-80.21º):
.....
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateX(-1.7),
alignment: Alignment.topCenter,
.....
Pulling the door, pushing the door
Previous examples were about rotating around X-axis, giving a 3D perspective. Now we will give a similar 3D view but rotating around the Y-axis. For that, we will implement pulling-the-door and pushing-the-door effects.
First, we implement the doorway (parent container).
Container( //doorway
margin: const EdgeInsets.only(left: 60, top: 60),
width: 200,
height: 400,
color: Colors.black87,
)
Then add the door container as the doorway container's child (initially hiding the doorway completely).
Container( //doorway
margin: const EdgeInsets.only(left: 60, top: 60),
width: 200,
height: 400,
color: Colors.black87,
child: Container( //door
width: 300,
height: 400,
color: Colors.orangeAccent,
alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 30),
child: Container( // doorknob
decoration: BoxDecoration(
color: Colors.deepOrange,
shape: BoxShape.circle,
),
width: 30,
height: 30,
),
),
)
And finally, the pulling-the-door effect by rotating it around Y-axis (door left side) in 3D perspective:
Container( // doorway
margin: const EdgeInsets.only(left: 60, top: 60),
width: 200,
height: 400,
color: Colors.black87,
child: Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(0.7),
alignment: Alignment.centerLeft,
child: Container( // door
width: 300,
height: 400,
color: Colors.orangeAccent,
alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 30),
child: Container(
decoration: BoxDecoration(
color: Colors.deepOrange,
shape: BoxShape.circle,
),
width: 30,
height: 30,
),
),
),
)
Besides using: rotateY()
, notice we make the rotation around the left side of the door, doing:alignment: Alignment.centerLeft
And if we use a negative angle instead of a positive one, we get the effect of pushing the door:
Playing with perspective parameter.
We will call the value in position (3,2) of the transformation matrix the perspective parameter.
In the previous examples, we have used a value of 0.001 for this element:transform: Matrix4.identity()..setEntry(3,2,0.001)
As we have seen, this is value is the reason we see the 3D perspective.
Let’s see some examples with a fixed angle of rotation but changing the perspective parameter.
Before being rotated:
Fixed angle of rotation = -0.9 radians and a perspective param =0.001, 0.002, 0.003 and 0.004.
If you have found this article enjoyable or it has helped you in some way, give me many 👏.Thanks for reading me!