Flutter Flip Card Animation With 3D Effect

Implementing Card Flip Animation In Flutter will not be hard as you think because of the Widgets and Classes provided by Flutter. 

I will teach you how to implement Flip card animation to your next flutter project from scratch. At the end of the article, you will have beautiful 3D Flip card animation with a few lines of code

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 occur. 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,
                      ),
                    )
    

Implementing Flip Card Animation

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,
                      ),
                    ),
    
flutter flip card

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. 

You Need To Know
Comments
  1. Thanks for the article!
    There’s a couple things that don’t work out of the box however:
    * Animation & Tween types defaulted to `int`, which crashes the whole app due to a type mismatch. The type they work on can be forced to “
    * The `Tween` that you are using does not serve any purpose as far as I see, because you are still using the 0-1 range. It can be entirely removed, if you use the existing AnimationController instead. Any reason to keep it?

Leave a Reply

Your email address will not be published. Required fields are marked *

x