Created
April 3, 2019 23:46
-
-
Save gbuela/64557c130d62e23d33b3e47d076e95e2 to your computer and use it in GitHub Desktop.
Card flip animation in Flutter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:math'; | |
import 'package:flutter/material.dart'; | |
// based on https://github.com/fedeoo/flip_card/ | |
// provides a callback to the front & back widgets so they can trigger the flip, therefore the CardSideBuilder | |
// note this version only flips horizontally | |
class AnimationCard extends StatelessWidget { | |
AnimationCard({this.child, this.animation}); | |
final Widget child; | |
final Animation<double> animation; | |
@override | |
Widget build(BuildContext context) { | |
return AnimatedBuilder( | |
animation: animation, | |
builder: (BuildContext context, Widget child) { | |
var transform = Matrix4.identity(); | |
transform.setEntry(3, 2, 0.001); | |
transform.rotateY(animation.value); | |
return Transform( | |
transform: transform, | |
alignment: Alignment.center, | |
child: child, | |
); | |
}, | |
child: child, | |
); | |
} | |
} | |
typedef Widget CardSideBuilder( | |
BuildContext context, VoidCallback toggleCallback); | |
class FlipCard extends StatefulWidget { | |
final CardSideBuilder frontBuilder; | |
final CardSideBuilder backBuilder; | |
final int speed = 300; | |
const FlipCard( | |
{Key key, @required this.frontBuilder, @required this.backBuilder}) | |
: super(key: key); | |
@override | |
State<StatefulWidget> createState() { | |
return _FlipCardState(); | |
} | |
} | |
enum FlippingState { Front, FrontOut, BackIn, Back, BackOut, FrontIn } | |
class _FlipCardState extends State<FlipCard> with TickerProviderStateMixin { | |
AnimationController controllerFront; | |
AnimationController controllerBack; | |
Animation<double> _frontOutRotation; | |
Animation<double> _backInRotation; | |
FlippingState _flippingState; | |
@override | |
void initState() { | |
super.initState(); | |
_flippingState = FlippingState.Front; | |
controllerFront = AnimationController( | |
duration: Duration(milliseconds: widget.speed), vsync: this); | |
controllerBack = AnimationController( | |
duration: Duration(milliseconds: widget.speed), vsync: this); | |
_frontOutRotation = TweenSequence( | |
<TweenSequenceItem<double>>[ | |
TweenSequenceItem<double>( | |
tween: Tween(begin: 0.0, end: pi / 2) | |
.chain(CurveTween(curve: Curves.linear)), | |
weight: 50.0, | |
), | |
], | |
).animate(controllerFront); | |
_backInRotation = TweenSequence( | |
<TweenSequenceItem<double>>[ | |
TweenSequenceItem<double>( | |
tween: Tween(begin: -pi / 2, end: 0.0) | |
.chain(CurveTween(curve: Curves.linear)), | |
weight: 50.0, | |
), | |
], | |
).animate(controllerBack); | |
} | |
_toggleCard() { | |
if (_flippingState == FlippingState.Front) { | |
setState(() { | |
_flippingState = FlippingState.FrontOut; | |
}); | |
} else if (_flippingState == FlippingState.Back) { | |
setState(() { | |
_flippingState = FlippingState.BackOut; | |
}); | |
} | |
} | |
Widget _animatedFront(BuildContext context) { | |
return AnimationCard( | |
animation: _frontOutRotation, | |
child: widget.frontBuilder(context, _toggleCard), | |
); | |
} | |
Widget _animatedBack(BuildContext context) { | |
return AnimationCard( | |
animation: _backInRotation, | |
child: widget.backBuilder(context, _toggleCard), | |
); | |
} | |
@override | |
Widget build(BuildContext context) { | |
switch (_flippingState) { | |
case FlippingState.Front: | |
return widget.frontBuilder(context, _toggleCard); | |
case FlippingState.Back: | |
return widget.backBuilder(context, _toggleCard); | |
case FlippingState.FrontOut: | |
controllerFront.forward().whenComplete(() { | |
setState(() { | |
_flippingState = FlippingState.BackIn; | |
}); | |
}); | |
return _animatedFront(context); | |
case FlippingState.BackIn: | |
controllerBack.forward().whenComplete(() { | |
setState(() { | |
_flippingState = FlippingState.Back; | |
}); | |
}); | |
return _animatedBack(context); | |
case FlippingState.BackOut: | |
controllerBack.reverse().whenComplete(() { | |
setState(() { | |
_flippingState = FlippingState.FrontIn; | |
}); | |
}); | |
return _animatedBack(context); | |
case FlippingState.FrontIn: | |
controllerFront.reverse().whenComplete(() { | |
setState(() { | |
_flippingState = FlippingState.Front; | |
}); | |
}); | |
return _animatedFront(context); | |
} | |
} | |
@override | |
void dispose() { | |
controllerFront.dispose(); | |
controllerBack.dispose(); | |
super.dispose(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can it be improved? let me know!