Created
July 8, 2019 16:29
-
-
Save DaniyarGilimov/e152c02cdf184c905dbcfe8e838b7097 to your computer and use it in GitHub Desktop.
Card widget, it's like a real playing card
This file contains 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 'package:flutter/material.dart'; | |
class CardItem extends StatefulWidget { | |
CardItem({Key key, this.color, this.value}) : super(key: key); | |
final int value; | |
final Color color; | |
_CardItemState createState() => _CardItemState(); | |
} | |
class _CardItemState extends State<CardItem> with TickerProviderStateMixin { | |
// Animation used to slide the card to some position, after dragging ends | |
AnimationController controller; | |
Animation<double> animation; | |
bool isDragging = false; //shows if card is dragging by finger | |
bool isAnimating = false; //shows if card is thowing(sliding) | |
double x; //card position in X axis | |
double y; //card position in Y axis | |
// Animation used to center the card, after finger touches card | |
AnimationController controllerCentering; | |
Animation<double> animationCentering; | |
//varibles used to center the card, after finger touches card | |
double dx = 0; | |
double dy = 0; | |
double dxx = 0; | |
double dyy = 0; | |
// Current position of finger while dragging | |
double currentPanPositionX = 0; | |
double currentPanPositionY = 0; | |
// Position of finger when it starts dragging | |
double firstPanPositionX = 0; | |
double firstPanPositionY = 0; | |
@override | |
void initState() { | |
controller = AnimationController( | |
duration: const Duration(milliseconds: 500), vsync: this); | |
animation = Tween<double>(begin: 0, end: 250) | |
.chain(new CurveTween( | |
curve: Curves.easeOutQuad, | |
)) | |
.animate(controller) | |
..addListener(() {}) | |
..addStatusListener((AnimationStatus status) { | |
if (status == AnimationStatus.completed) { | |
setState(() { | |
isAnimating = false; | |
}); | |
} | |
}); | |
controllerCentering = AnimationController( | |
duration: const Duration(milliseconds: 500), vsync: this); | |
animationCentering = Tween<double>(begin: 0, end: 1) | |
.chain(new CurveTween( | |
curve: Curves.easeOutQuad, | |
)) | |
.animate(controller) | |
..addListener(() {}) | |
..addStatusListener((AnimationStatus status) { | |
if (status == AnimationStatus.completed) { | |
setState(() {}); | |
} | |
}); | |
isDragging = false; | |
x = 0; | |
y = 0; | |
super.initState(); | |
} | |
@override | |
void dispose() { | |
controller.dispose(); | |
super.dispose(); | |
} | |
// dragging card | |
void _onTapUpdate(DragUpdateDetails details) { | |
if (isAnimating) return; | |
setState(() { | |
// setting finger position to card position, dx and dy describes centering the card after first touch | |
x = details.globalPosition.dx - dx; | |
y = details.globalPosition.dy - dy; | |
//setting current drag position | |
currentPanPositionX = details.globalPosition.dx; | |
currentPanPositionY = details.globalPosition.dy; | |
isDragging = true; | |
isAnimating = false; | |
}); | |
} | |
//when finger touches the card | |
void _onPanStart(DragStartDetails startDetails) { | |
setState(() { | |
//setting finger first touch details | |
firstPanPositionX = startDetails.globalPosition.dx; | |
firstPanPositionY = startDetails.globalPosition.dy; | |
currentPanPositionX = firstPanPositionX; | |
currentPanPositionY = firstPanPositionY; | |
//centering the card | |
dxx = (x - firstPanPositionX).abs(); | |
dyy = (y - firstPanPositionY).abs(); | |
animationCentering = Tween<double>(begin: 0, end: 1) | |
.chain(new CurveTween( | |
curve: Curves.easeOutQuad, | |
)) | |
.animate(controllerCentering) | |
..addListener(() { | |
setState(() { | |
dx = dxx + (37.5 - dxx) * animationCentering.value; | |
dy = dyy + (175 - dyy) * animationCentering.value; | |
x = currentPanPositionX - dx; | |
y = currentPanPositionY - dy; | |
}); | |
}) | |
..addStatusListener((AnimationStatus status) { | |
if (status == AnimationStatus.completed) { | |
dx = 37.5; | |
dy = 175; | |
} | |
}); | |
controllerCentering.forward(from: 0.0); | |
}); | |
} | |
//when dragging is ended, and card slides to some position, in our case to endPointX | |
void _onPanEnd(DragEndDetails endDetails) { | |
double endPointX; | |
// checking if card position on left side or on right side | |
if (x < MediaQuery.of(context).size.width / 2) { | |
endPointX = 0; | |
} else { | |
endPointX = MediaQuery.of(context).size.width - 70; | |
} | |
setState(() { | |
isDragging = false; | |
isAnimating = true; | |
animation = Tween<double>(begin: x, end: endPointX) | |
.chain(new CurveTween( | |
curve: Curves.easeOutQuad, | |
)) | |
.animate(controller) | |
..addListener(() { | |
setState(() {}); | |
}) | |
..addStatusListener((AnimationStatus status) { | |
if (status == AnimationStatus.completed) { | |
setState(() { | |
isAnimating = false; | |
x = animation.value; | |
}); | |
} | |
}); | |
controller.forward(from: 0.0); | |
isDragging = false; | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Positioned( | |
left: (isDragging) ? x : (isAnimating) ? animation.value : x, | |
top: y, | |
child: SizedBox( | |
width: 75, | |
height: 125, | |
child: GestureDetector( | |
onPanUpdate: (DragUpdateDetails details) { | |
if (!isAnimating) { | |
_onTapUpdate(details); | |
} | |
}, | |
onPanEnd: (DragEndDetails endDetails) => _onPanEnd(endDetails), | |
onPanStart: (DragStartDetails startDetails) { | |
if (!isAnimating) { | |
_onPanStart(startDetails); | |
} | |
}, | |
child: Container( | |
color: widget.color, | |
width: double.infinity, | |
height: double.infinity, | |
), | |
)), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment