DEV Community

Cover image for Flutter Flip Card Animation With 3D Effect
Mighty
Mighty

Posted on • Edited on • Originally published at mightytechno.com

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.

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,
                      ),
                    )
Enter fullscreen mode Exit fullscreen mode

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;
          });
      }
Enter fullscreen mode Exit fullscreen mode

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 {
Enter fullscreen mode Exit fullscreen mode

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,
                      ),
                    )
                  ,
            )
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

Flutter logs

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,
                          ),
                        ),
                ),
              ),
            ),
          ),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

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

Connect with me - Instagram |Blog |Twitter

Top comments (5)

Collapse
 
manudevcode profile image
Manuel Alejandro

_TypeError (type 'int' is not a subtype of type 'double') on rotateX params

Collapse
 
ngaretou profile image
ngaretou • Edited

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.

Collapse
 
legendree profile image
Legendree • Edited

Tween in your initState()

Collapse
 
chitgoks profile image
chitgoks

Do you have one for horizontal?

Collapse
 
ngaretou profile image
ngaretou

Try changing this line:
..rotateX(pi * _animation.value),
to:
..rotateY(pi * _animation.value),