refactor: add an animation layer to split responsibilities
This commit is contained in:
parent
2d378c7d65
commit
8330f0bc80
|
|
@ -0,0 +1,165 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_card_swiper/flutter_card_swiper.dart';
|
||||||
|
import 'package:flutter_card_swiper/src/extensions.dart';
|
||||||
|
|
||||||
|
class CardAnimation {
|
||||||
|
CardAnimation({
|
||||||
|
required this.animationController,
|
||||||
|
required this.maxAngle,
|
||||||
|
required this.initialScale,
|
||||||
|
this.isHorizontalSwipingEnabled = true,
|
||||||
|
this.isVerticalSwipingEnabled = true,
|
||||||
|
}) : scale = initialScale;
|
||||||
|
|
||||||
|
final double maxAngle;
|
||||||
|
final double initialScale;
|
||||||
|
final AnimationController animationController;
|
||||||
|
final bool isHorizontalSwipingEnabled;
|
||||||
|
final bool isVerticalSwipingEnabled;
|
||||||
|
|
||||||
|
double left = 0;
|
||||||
|
double top = 0;
|
||||||
|
double total = 0;
|
||||||
|
double angle = 0;
|
||||||
|
double scale;
|
||||||
|
double difference = 40;
|
||||||
|
|
||||||
|
late Animation<double> _leftAnimation;
|
||||||
|
late Animation<double> _topAnimation;
|
||||||
|
late Animation<double> _scaleAnimation;
|
||||||
|
late Animation<double> _differenceAnimation;
|
||||||
|
|
||||||
|
double get _maxAngleInRadian => maxAngle * (pi / 180);
|
||||||
|
|
||||||
|
void sync() {
|
||||||
|
left = _leftAnimation.value;
|
||||||
|
top = _topAnimation.value;
|
||||||
|
scale = _scaleAnimation.value;
|
||||||
|
difference = _differenceAnimation.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
animationController.reset();
|
||||||
|
left = 0;
|
||||||
|
top = 0;
|
||||||
|
total = 0;
|
||||||
|
angle = 0;
|
||||||
|
scale = initialScale;
|
||||||
|
difference = 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(double dx, double dy, bool inverseAngle) {
|
||||||
|
if (isHorizontalSwipingEnabled) {
|
||||||
|
left += dx;
|
||||||
|
}
|
||||||
|
if (isVerticalSwipingEnabled) {
|
||||||
|
top += dy;
|
||||||
|
}
|
||||||
|
total = left + top;
|
||||||
|
updateAngle(inverseAngle);
|
||||||
|
updateScale();
|
||||||
|
updateDifference();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateAngle(bool inverse) {
|
||||||
|
if (angle.isBetween(-_maxAngleInRadian, _maxAngleInRadian)) {
|
||||||
|
angle = _maxAngleInRadian * left / 1000;
|
||||||
|
if (inverse) angle *= -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateScale() {
|
||||||
|
if (scale.isBetween(initialScale, 1.0)) {
|
||||||
|
scale = (total > 0)
|
||||||
|
? initialScale + (total / 5000)
|
||||||
|
: initialScale + -(total / 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateDifference() {
|
||||||
|
if (difference.isBetween(0, difference)) {
|
||||||
|
difference = (total > 0) ? 40 - (total / 10) : 40 + (total / 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void animate(BuildContext context, CardSwiperDirection direction) {
|
||||||
|
switch (direction) {
|
||||||
|
case CardSwiperDirection.left:
|
||||||
|
return animateHorizontally(context, false);
|
||||||
|
case CardSwiperDirection.right:
|
||||||
|
return animateHorizontally(context, true);
|
||||||
|
case CardSwiperDirection.top:
|
||||||
|
return animateVertically(context, false);
|
||||||
|
case CardSwiperDirection.bottom:
|
||||||
|
return animateVertically(context, true);
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void animateHorizontally(BuildContext context, bool isToRight) {
|
||||||
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
|
_leftAnimation = Tween<double>(
|
||||||
|
begin: left,
|
||||||
|
end: isToRight ? screenWidth : -screenWidth,
|
||||||
|
).animate(animationController);
|
||||||
|
_topAnimation = Tween<double>(
|
||||||
|
begin: top,
|
||||||
|
end: top + top,
|
||||||
|
).animate(animationController);
|
||||||
|
_scaleAnimation = Tween<double>(
|
||||||
|
begin: scale,
|
||||||
|
end: 1.0,
|
||||||
|
).animate(animationController);
|
||||||
|
_differenceAnimation = Tween<double>(
|
||||||
|
begin: difference,
|
||||||
|
end: 0,
|
||||||
|
).animate(animationController);
|
||||||
|
animationController.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
void animateVertically(BuildContext context, bool isToBottom) {
|
||||||
|
final screenHeight = MediaQuery.of(context).size.height;
|
||||||
|
|
||||||
|
_leftAnimation = Tween<double>(
|
||||||
|
begin: left,
|
||||||
|
end: left + left,
|
||||||
|
).animate(animationController);
|
||||||
|
_topAnimation = Tween<double>(
|
||||||
|
begin: top,
|
||||||
|
end: isToBottom ? screenHeight : -screenHeight,
|
||||||
|
).animate(animationController);
|
||||||
|
_scaleAnimation = Tween<double>(
|
||||||
|
begin: scale,
|
||||||
|
end: 1.0,
|
||||||
|
).animate(animationController);
|
||||||
|
_differenceAnimation = Tween<double>(
|
||||||
|
begin: difference,
|
||||||
|
end: 0,
|
||||||
|
).animate(animationController);
|
||||||
|
animationController.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
void animateBack(BuildContext context) {
|
||||||
|
_leftAnimation = Tween<double>(
|
||||||
|
begin: left,
|
||||||
|
end: 0,
|
||||||
|
).animate(animationController);
|
||||||
|
_topAnimation = Tween<double>(
|
||||||
|
begin: top,
|
||||||
|
end: 0,
|
||||||
|
).animate(animationController);
|
||||||
|
_scaleAnimation = Tween<double>(
|
||||||
|
begin: scale,
|
||||||
|
end: initialScale,
|
||||||
|
).animate(animationController);
|
||||||
|
_differenceAnimation = Tween<double>(
|
||||||
|
begin: difference,
|
||||||
|
end: 40,
|
||||||
|
).animate(animationController);
|
||||||
|
animationController.forward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_card_swiper/src/card_animation.dart';
|
||||||
import 'package:flutter_card_swiper/src/card_swiper_controller.dart';
|
import 'package:flutter_card_swiper/src/card_swiper_controller.dart';
|
||||||
import 'package:flutter_card_swiper/src/enums.dart';
|
import 'package:flutter_card_swiper/src/enums.dart';
|
||||||
import 'package:flutter_card_swiper/src/extensions.dart';
|
import 'package:flutter_card_swiper/src/extensions.dart';
|
||||||
|
|
@ -148,32 +149,18 @@ class CardSwiper extends StatefulWidget {
|
||||||
|
|
||||||
class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
double _left = 0;
|
late CardAnimation _cardAnimation;
|
||||||
double _top = 0;
|
late AnimationController _animationController;
|
||||||
double _total = 0;
|
|
||||||
double _angle = 0;
|
|
||||||
late double _scale = widget.scale;
|
|
||||||
double _difference = 40;
|
|
||||||
|
|
||||||
SwipeType _swipeType = SwipeType.none;
|
SwipeType _swipeType = SwipeType.none;
|
||||||
bool _tapOnTop = false; //position of starting drag point on card
|
CardSwiperDirection _detectedDirection = CardSwiperDirection.none;
|
||||||
|
bool _tappedOnTop = false;
|
||||||
late AnimationController _animationController;
|
|
||||||
late Animation<double> _leftAnimation;
|
|
||||||
late Animation<double> _topAnimation;
|
|
||||||
late Animation<double> _scaleAnimation;
|
|
||||||
late Animation<double> _differenceAnimation;
|
|
||||||
|
|
||||||
CardSwiperDirection detectedDirection = CardSwiperDirection.none;
|
|
||||||
|
|
||||||
double get _maxAngle => widget.maxAngle * (pi / 180);
|
|
||||||
|
|
||||||
int? _currentIndex;
|
|
||||||
|
|
||||||
int? get _nextIndex => getValidIndexOffset(1);
|
|
||||||
|
|
||||||
bool get _canSwipe => _currentIndex != null && !widget.isDisabled;
|
bool get _canSwipe => _currentIndex != null && !widget.isDisabled;
|
||||||
|
|
||||||
|
int? _currentIndex;
|
||||||
|
int? get _nextIndex => getValidIndexOffset(1);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
@ -188,6 +175,12 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
||||||
)
|
)
|
||||||
..addListener(_animationListener)
|
..addListener(_animationListener)
|
||||||
..addStatusListener(_animationStatusListener);
|
..addStatusListener(_animationStatusListener);
|
||||||
|
|
||||||
|
_cardAnimation = CardAnimation(
|
||||||
|
animationController: _animationController,
|
||||||
|
maxAngle: widget.maxAngle,
|
||||||
|
initialScale: widget.scale,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -208,7 +201,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
||||||
return Stack(
|
return Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: List.generate(nbOfCardsOnScreen(), (index) {
|
children: List.generate(numberOfCardsOnScreen(), (index) {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return _frontItem(constraints);
|
return _frontItem(constraints);
|
||||||
}
|
}
|
||||||
|
|
@ -225,14 +218,13 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The card shown at the front of the stack, that can be dragged and swipped
|
|
||||||
Widget _frontItem(BoxConstraints constraints) {
|
Widget _frontItem(BoxConstraints constraints) {
|
||||||
return Positioned(
|
return Positioned(
|
||||||
left: _left,
|
left: _cardAnimation.left,
|
||||||
top: _top,
|
top: _cardAnimation.top,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: Transform.rotate(
|
child: Transform.rotate(
|
||||||
angle: _angle,
|
angle: _cardAnimation.angle,
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: constraints,
|
constraints: constraints,
|
||||||
child: widget.cardBuilder(context, _currentIndex!),
|
child: widget.cardBuilder(context, _currentIndex!),
|
||||||
|
|
@ -248,44 +240,36 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
||||||
final renderBox = context.findRenderObject()! as RenderBox;
|
final renderBox = context.findRenderObject()! as RenderBox;
|
||||||
final position = renderBox.globalToLocal(tapInfo.globalPosition);
|
final position = renderBox.globalToLocal(tapInfo.globalPosition);
|
||||||
|
|
||||||
if (position.dy < renderBox.size.height / 2) _tapOnTop = true;
|
if (position.dy < renderBox.size.height / 2) _tappedOnTop = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPanUpdate: (tapInfo) {
|
onPanUpdate: (tapInfo) {
|
||||||
if (!widget.isDisabled) {
|
if (!widget.isDisabled) {
|
||||||
setState(() {
|
setState(
|
||||||
if (widget.isHorizontalSwipingEnabled) {
|
() => _cardAnimation.update(
|
||||||
_left += tapInfo.delta.dx;
|
tapInfo.delta.dx,
|
||||||
}
|
tapInfo.delta.dy,
|
||||||
if (widget.isVerticalSwipingEnabled) {
|
_tappedOnTop,
|
||||||
_top += tapInfo.delta.dy;
|
),
|
||||||
}
|
);
|
||||||
_total = _left + _top;
|
|
||||||
_calculateAngle();
|
|
||||||
_calculateScale();
|
|
||||||
_calculateDifference();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPanEnd: (tapInfo) {
|
onPanEnd: (tapInfo) {
|
||||||
if (_canSwipe) {
|
if (_canSwipe) {
|
||||||
_tapOnTop = false;
|
_tappedOnTop = false;
|
||||||
_onEndAnimation();
|
_onEndAnimation();
|
||||||
_animationController.forward();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the card that is just behind the _frontItem, only moves to take its place
|
|
||||||
/// during a movement of _frontItem
|
|
||||||
Widget _secondItem(BoxConstraints constraints) {
|
Widget _secondItem(BoxConstraints constraints) {
|
||||||
return Positioned(
|
return Positioned(
|
||||||
top: _difference,
|
top: _cardAnimation.difference,
|
||||||
left: 0,
|
left: 0,
|
||||||
child: Transform.scale(
|
child: Transform.scale(
|
||||||
scale: _scale,
|
scale: _cardAnimation.scale,
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: constraints,
|
constraints: constraints,
|
||||||
child: widget.cardBuilder(context, _nextIndex!),
|
child: widget.cardBuilder(context, _nextIndex!),
|
||||||
|
|
@ -294,8 +278,6 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// if widget.numberOfCardsDisplayed > 2, those cards are built behind the
|
|
||||||
/// _secondItem and can't move at all
|
|
||||||
Widget _backItem(BoxConstraints constraints, int offset) {
|
Widget _backItem(BoxConstraints constraints, int offset) {
|
||||||
return Positioned(
|
return Positioned(
|
||||||
top: 40,
|
top: 40,
|
||||||
|
|
@ -313,229 +295,99 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//swipe widget from the outside
|
|
||||||
void _controllerListener() {
|
void _controllerListener() {
|
||||||
switch (widget.controller!.state) {
|
switch (widget.controller?.state) {
|
||||||
case CardSwiperState.swipe:
|
case CardSwiperState.swipe:
|
||||||
_swipe(context, widget.direction);
|
return _swipe(widget.direction);
|
||||||
break;
|
|
||||||
case CardSwiperState.swipeLeft:
|
case CardSwiperState.swipeLeft:
|
||||||
_swipe(context, CardSwiperDirection.left);
|
return _swipe(CardSwiperDirection.left);
|
||||||
break;
|
|
||||||
case CardSwiperState.swipeRight:
|
case CardSwiperState.swipeRight:
|
||||||
_swipe(context, CardSwiperDirection.right);
|
return _swipe(CardSwiperDirection.right);
|
||||||
break;
|
|
||||||
case CardSwiperState.swipeTop:
|
case CardSwiperState.swipeTop:
|
||||||
_swipe(context, CardSwiperDirection.top);
|
return _swipe(CardSwiperDirection.top);
|
||||||
break;
|
|
||||||
case CardSwiperState.swipeBottom:
|
case CardSwiperState.swipeBottom:
|
||||||
_swipe(context, CardSwiperDirection.bottom);
|
return _swipe(CardSwiperDirection.bottom);
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//when value of controller changes
|
|
||||||
void _animationListener() {
|
void _animationListener() {
|
||||||
if (_animationController.status == AnimationStatus.forward) {
|
if (_animationController.status == AnimationStatus.forward) {
|
||||||
setState(() {
|
setState(_cardAnimation.sync);
|
||||||
_left = _leftAnimation.value;
|
|
||||||
_top = _topAnimation.value;
|
|
||||||
_scale = _scaleAnimation.value;
|
|
||||||
_difference = _differenceAnimation.value;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle the onSwipe methode as well as removing the current card from the
|
void _animationStatusListener(AnimationStatus status) {
|
||||||
// stack if onSwipe does not return false
|
if (status == AnimationStatus.completed) {
|
||||||
void _handleOnSwipe() {
|
switch (_swipeType) {
|
||||||
setState(() {
|
case SwipeType.swipe:
|
||||||
if (_swipeType == SwipeType.swipe) {
|
_handleCompleteSwipe();
|
||||||
final shouldCancelSwipe = widget.onSwipe
|
break;
|
||||||
?.call(_currentIndex, _nextIndex, detectedDirection) ==
|
default:
|
||||||
false;
|
break;
|
||||||
|
|
||||||
if (shouldCancelSwipe) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentIndex = _nextIndex;
|
|
||||||
|
|
||||||
final isLastCard = _currentIndex == widget.cardsCount - 1;
|
|
||||||
if (isLastCard) {
|
|
||||||
widget.onEnd?.call();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
_reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset the card animation
|
void _handleCompleteSwipe() {
|
||||||
void _resetCardAnimation() {
|
final shouldCancelSwipe =
|
||||||
|
widget.onSwipe?.call(_currentIndex, _nextIndex, _detectedDirection) ==
|
||||||
|
false;
|
||||||
|
|
||||||
|
if (shouldCancelSwipe) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentIndex = _nextIndex;
|
||||||
|
|
||||||
|
final isLastCard = _currentIndex == widget.cardsCount - 1;
|
||||||
|
if (isLastCard) {
|
||||||
|
widget.onEnd?.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _reset() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_animationController.reset();
|
_animationController.reset();
|
||||||
_left = 0;
|
_cardAnimation.reset();
|
||||||
_top = 0;
|
|
||||||
_total = 0;
|
|
||||||
_angle = 0;
|
|
||||||
_scale = widget.scale;
|
|
||||||
_difference = 40;
|
|
||||||
_swipeType = SwipeType.none;
|
_swipeType = SwipeType.none;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//when the status of animation changes
|
|
||||||
void _animationStatusListener(AnimationStatus status) {
|
|
||||||
if (status == AnimationStatus.completed) {
|
|
||||||
_handleOnSwipe();
|
|
||||||
_resetCardAnimation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _calculateAngle() {
|
|
||||||
if (_angle <= _maxAngle && _angle >= -_maxAngle) {
|
|
||||||
_angle = (_maxAngle / 100) * (_left / 10);
|
|
||||||
if (_tapOnTop) _angle *= -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _calculateScale() {
|
|
||||||
if (_scale <= 1.0 && _scale >= widget.scale) {
|
|
||||||
_scale = (_total > 0)
|
|
||||||
? widget.scale + (_total / 5000)
|
|
||||||
: widget.scale + -1 * (_total / 5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _calculateDifference() {
|
|
||||||
if (_difference >= 0 && _difference <= _difference) {
|
|
||||||
_difference = (_total > 0) ? 40 - (_total / 10) : 40 + (_total / 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onEndAnimation() {
|
void _onEndAnimation() {
|
||||||
if (_left < -widget.threshold || _left > widget.threshold) {
|
if (_cardAnimation.left.abs() > widget.threshold) {
|
||||||
_swipeHorizontal(context);
|
final direction = _cardAnimation.left.isNegative
|
||||||
} else if (_top < -widget.threshold || _top > widget.threshold) {
|
? CardSwiperDirection.left
|
||||||
_swipeVertical(context);
|
: CardSwiperDirection.right;
|
||||||
|
_swipe(direction);
|
||||||
|
} else if (_cardAnimation.top.abs() > widget.threshold) {
|
||||||
|
final direction = _cardAnimation.top.isNegative
|
||||||
|
? CardSwiperDirection.top
|
||||||
|
: CardSwiperDirection.bottom;
|
||||||
|
_swipe(direction);
|
||||||
} else {
|
} else {
|
||||||
_goBack(context);
|
_goBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _swipe(BuildContext context, CardSwiperDirection direction) {
|
void _swipe(CardSwiperDirection direction) {
|
||||||
if (!_canSwipe) return;
|
if (!_canSwipe) return;
|
||||||
|
|
||||||
switch (direction) {
|
|
||||||
case CardSwiperDirection.left:
|
|
||||||
_left = -1;
|
|
||||||
_swipeHorizontal(context);
|
|
||||||
break;
|
|
||||||
case CardSwiperDirection.right:
|
|
||||||
_left = widget.threshold + 1;
|
|
||||||
_swipeHorizontal(context);
|
|
||||||
break;
|
|
||||||
case CardSwiperDirection.top:
|
|
||||||
_top = -1;
|
|
||||||
_swipeVertical(context);
|
|
||||||
break;
|
|
||||||
case CardSwiperDirection.bottom:
|
|
||||||
_top = widget.threshold + 1;
|
|
||||||
_swipeVertical(context);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_animationController.forward();
|
|
||||||
}
|
|
||||||
|
|
||||||
//moves the card away to the left or right
|
|
||||||
void _swipeHorizontal(BuildContext context) {
|
|
||||||
_leftAnimation = Tween<double>(
|
|
||||||
begin: _left,
|
|
||||||
end: (_left == 0 && widget.direction == CardSwiperDirection.right) ||
|
|
||||||
_left > widget.threshold
|
|
||||||
? MediaQuery.of(context).size.width
|
|
||||||
: -MediaQuery.of(context).size.width,
|
|
||||||
).animate(_animationController);
|
|
||||||
_topAnimation = Tween<double>(
|
|
||||||
begin: _top,
|
|
||||||
end: _top + _top,
|
|
||||||
).animate(_animationController);
|
|
||||||
_scaleAnimation = Tween<double>(
|
|
||||||
begin: _scale,
|
|
||||||
end: 1.0,
|
|
||||||
).animate(_animationController);
|
|
||||||
_differenceAnimation = Tween<double>(
|
|
||||||
begin: _difference,
|
|
||||||
end: 0,
|
|
||||||
).animate(_animationController);
|
|
||||||
|
|
||||||
_swipeType = SwipeType.swipe;
|
_swipeType = SwipeType.swipe;
|
||||||
if (_left > widget.threshold ||
|
_detectedDirection = direction;
|
||||||
_left == 0 && widget.direction == CardSwiperDirection.right) {
|
_cardAnimation.animate(context, direction);
|
||||||
detectedDirection = CardSwiperDirection.right;
|
|
||||||
} else {
|
|
||||||
detectedDirection = CardSwiperDirection.left;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//moves the card away to the top or bottom
|
void _goBack() {
|
||||||
void _swipeVertical(BuildContext context) {
|
|
||||||
_leftAnimation = Tween<double>(
|
|
||||||
begin: _left,
|
|
||||||
end: _left + _left,
|
|
||||||
).animate(_animationController);
|
|
||||||
_topAnimation = Tween<double>(
|
|
||||||
begin: _top,
|
|
||||||
end: (_top == 0 && widget.direction == CardSwiperDirection.bottom) ||
|
|
||||||
_top > widget.threshold
|
|
||||||
? MediaQuery.of(context).size.height
|
|
||||||
: -MediaQuery.of(context).size.height,
|
|
||||||
).animate(_animationController);
|
|
||||||
_scaleAnimation = Tween<double>(
|
|
||||||
begin: _scale,
|
|
||||||
end: 1.0,
|
|
||||||
).animate(_animationController);
|
|
||||||
_differenceAnimation = Tween<double>(
|
|
||||||
begin: _difference,
|
|
||||||
end: 0,
|
|
||||||
).animate(_animationController);
|
|
||||||
|
|
||||||
_swipeType = SwipeType.swipe;
|
|
||||||
if (_top > widget.threshold ||
|
|
||||||
_top == 0 && widget.direction == CardSwiperDirection.bottom) {
|
|
||||||
detectedDirection = CardSwiperDirection.bottom;
|
|
||||||
} else {
|
|
||||||
detectedDirection = CardSwiperDirection.top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//moves the card back to starting position
|
|
||||||
void _goBack(BuildContext context) {
|
|
||||||
_leftAnimation = Tween<double>(
|
|
||||||
begin: _left,
|
|
||||||
end: 0,
|
|
||||||
).animate(_animationController);
|
|
||||||
_topAnimation = Tween<double>(
|
|
||||||
begin: _top,
|
|
||||||
end: 0,
|
|
||||||
).animate(_animationController);
|
|
||||||
_scaleAnimation = Tween<double>(
|
|
||||||
begin: _scale,
|
|
||||||
end: widget.scale,
|
|
||||||
).animate(_animationController);
|
|
||||||
_differenceAnimation = Tween<double>(
|
|
||||||
begin: _difference,
|
|
||||||
end: 40,
|
|
||||||
).animate(_animationController);
|
|
||||||
|
|
||||||
_swipeType = SwipeType.back;
|
_swipeType = SwipeType.back;
|
||||||
|
_detectedDirection = CardSwiperDirection.none;
|
||||||
|
_cardAnimation.animateBack(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
///the number of cards that are built on the screen
|
int numberOfCardsOnScreen() {
|
||||||
int nbOfCardsOnScreen() {
|
|
||||||
if (widget.isLoop) {
|
if (widget.isLoop) {
|
||||||
return widget.numberOfCardsDisplayed;
|
return widget.numberOfCardsDisplayed;
|
||||||
}
|
}
|
||||||
|
|
@ -555,7 +407,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
||||||
}
|
}
|
||||||
|
|
||||||
final index = _currentIndex! + offset;
|
final index = _currentIndex! + offset;
|
||||||
if (!widget.isLoop && !index.isBetween(0, widget.cardsCount)) {
|
if (!widget.isLoop && !index.isBetween(0, widget.cardsCount - 1)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return index % widget.cardsCount;
|
return index % widget.cardsCount;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
extension Range on num {
|
extension Range on num {
|
||||||
bool isBetween(num from, num to) {
|
bool isBetween(num from, num to) {
|
||||||
return from < this && this < to;
|
return from <= this && this <= to;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue