Implementing Card Flip Animation In Flutter will not be hard as you think because of the Widgets and Classes provided by Flutter.
Start with basic Container
To get started first you will need some kind of Container and we will use this as our main component when Animation. This is a basic container with the Icon in the Middle.
Container(
color: Colors.blueAccent,
width: 200,
height: 200,
child: Icon(
Icons.ac_unit,
color: Colors.white,
size: 50,
),
)
Implement Animation to Flip Card
When implementing an animation there are two things which we need, Animation class and AnimationController class. Animation class will do the actual Animation part and the AnimationController class will help to control the animation like whether we need to do forward or backwards or more.
Of course, we need to use a stateful widget for this implementation.
First Create a new object of AnimationController and Animation class. Also, we need to track the animation status to do flip in forward and backwards. For that, you can use AnimationStatus enum.
AnimationController _animationController;
Animation _animation;
AnimationStatus _animationStatus = AnimationStatus.dismissed;
@override
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(seconds: 1));
_animation = Tween(end: 1, begin: 0).animate(_animationController)
..addListener(() {
setState(() {});
})
..addStatusListener((status) {
_animationStatus = status;
});
}
For the animationController we have to provide vsync and it accept TickerProvider. That can be provided by SingleTickerProviderStateMixin. Because of that, we use SingleTickerProviderStateMixin mixin by using with keyword like below
class _FlipCardState extends State
with SingleTickerProviderStateMixin {
Duration property will define the duration of the animation between the start and stop. For this we use Tween Animation with begin value 0 and end as 1. addStatusListener method will set the status of the animation whether is dismissed, forward, reverse or completed. Dismissed mean the animation is stop in the beginning and that will be the initial value.
Make container Clickable
You can Wrap the container inside the GestureDetector widget to make it clickable. GestureDetector onTap will trigger when you tap the container and that where we need to start our animation. In there if the Animation is dismissed mean stop in start state, you can animate a container in forward. Which mean the Tween animation value will be changed in a forward manner.
GestureDetector(
onTap: () {
if (_animationStatus == AnimationStatus.dismissed) {
_animationController.forward();
} else {
_animationController.reverse();
}
},
child: Container(
color: Colors.blueAccent,
width: 200,
height: 200,
child: Icon(
Icons.ac_unit,
color: Colors.white,
size: 50,
),
)
,
)
Things not finished yet,
Add Transform with 3d Effect
The next thing you need to do is wrap the GestureDetector inside the Transform widget. Transform widget can use to add a different transform to child widget.
For the Flip animation, you need to rotate in x-axis from the middle. So we set alignment to centre and set some Matrix to transform property. You can just set 2D transform by setting only the rotateX. but if you need 3D kind of effect you have to setEntry method. You can change these values and play around to get a different effect and I think these values fit best for my case. I am not a math person and if you are you will have a better sense of these values.
Under the hood when the animation get started the value of animation which you can access from _animation.value get change from 0 to 1. So we are setting that value by multiplying with PI. PI is equal to 180 and from the start to the end we change that value from 0 to 180 and that exactly why it rotates 180 degrees.
Transform(
alignment: FractionalOffset.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.002)
..rotateX(pi * _animation.value),
child: GestureDetector(
onTap: () {
if (_animationStatus == AnimationStatus.dismissed) {
_animationController.forward();
} else {
_animationController.reverse();
}
},
child: Container(
color: Colors.blueAccent,
width: 200,
height: 200,
child: Icon(
Icons.ac_unit,
color: Colors.white,
size: 50,
),
)
,
),
)
Now the all animation and Flip card things are working as expected. But how can we change the card when it flips.
Add different card to front and Back
You can do a small trick to make it work. When Animation is in progress we know it changes values from 0 to 1. When this value comes to 0.5, the card which will be in a 90-degree angle. In that position, we can swipe the container. So if the animation value is less than 0.5 we use blue container and if not we use the red container. pretty easy right?
_animation.value <= 0.5
? Container(
color: Colors.blueAccent,
width: 200,
height: 200,
child: Icon(
Icons.ac_unit,
color: Colors.white,
size: 50,
),
)
: Container(
color: Colors.red,
width: 200,
height: 200,
child: Icon(
Icons.ac_unit,
color: Colors.white,
size: 50,
),
),
Now this is the Full Code.
class _FlipCardState extends State
with SingleTickerProviderStateMixin {
AnimationController _animationController;
Animation _animation;
AnimationStatus _animationStatus = AnimationStatus.dismissed;
@override
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(seconds: 1));
_animation = Tween(end: 1, begin: 0).animate(_animationController)
..addListener(() {
setState(() {});
})
..addStatusListener((status) {
_animationStatus = status;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Container(
color: Color.fromARGB(255, 27, 28, 30),
child: Center(
child: Transform(
alignment: FractionalOffset.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.002)
..rotateX(pi * _animation.value),
child: GestureDetector(
onTap: () {
if (_animationStatus == AnimationStatus.dismissed) {
_animationController.forward();
} else {
_animationController.reverse();
}
},
child: _animation.value <= 0.5
? Container(
color: Colors.blueAccent,
width: 200,
height: 200,
child: Icon(
Icons.ac_unit,
color: Colors.white,
size: 50,
),
)
: Container(
color: Colors.red,
width: 200,
height: 200,
child: Icon(
Icons.ac_unit,
color: Colors.white,
size: 50,
),
),
),
),
),
),
);
}
}
Conclusion
I hope you get an idea about how to implement Flip card animation in flutter and If you have any question please add a comment below.
Check the Video for More
Originally published at mightytechno
Top comments (5)
_TypeError (type 'int' is not a subtype of type 'double') on rotateX params
I had the same problem! Eventually tracked it down - change the fourth line down in the complete code:
Animation _animation;
to:
Animation<double> _animation;
That should do it.
It seems like markdown strips out the angle brackets and what's in them - something there I think must be the problem.
Tween in your initState()
Do you have one for horizontal?
Try changing this line:
..rotateX(pi * _animation.value),
to:
..rotateY(pi * _animation.value),