feat: add the functionality to allow swipes individually in left, right, up or/and down directions
This commit is contained in:
parent
c55edd8d44
commit
9976486d02
|
|
@ -3,6 +3,7 @@
|
|||
/// animations supporting Android, iOS, Web & Desktop.
|
||||
library flutter_card_swiper;
|
||||
|
||||
export 'package:flutter_card_swiper/src/card_swipe_direction.dart';
|
||||
export 'package:flutter_card_swiper/src/card_swiper.dart';
|
||||
export 'package:flutter_card_swiper/src/card_swiper_controller.dart';
|
||||
export 'package:flutter_card_swiper/src/enums.dart';
|
||||
|
|
|
|||
|
|
@ -10,16 +10,22 @@ class CardAnimation {
|
|||
required this.maxAngle,
|
||||
required this.initialScale,
|
||||
required this.initialOffset,
|
||||
this.isHorizontalSwipingEnabled = true,
|
||||
this.isVerticalSwipingEnabled = true,
|
||||
@Deprecated('Use [cardSwipeDirection] instead]')
|
||||
this.isHorizontalSwipingEnabled = true,
|
||||
@Deprecated('Use [cardSwipeDirection] instead]')
|
||||
this.isVerticalSwipingEnabled = true,
|
||||
this.cardSwipeDirection = const AllowedSwipeDirection.all(),
|
||||
}) : scale = initialScale;
|
||||
|
||||
final double maxAngle;
|
||||
final double initialScale;
|
||||
final Offset initialOffset;
|
||||
final AnimationController animationController;
|
||||
@Deprecated('Use [cardSwipeDirection] instead]')
|
||||
final bool isHorizontalSwipingEnabled;
|
||||
@Deprecated('Use [cardSwipeDirection] instead]')
|
||||
final bool isVerticalSwipingEnabled;
|
||||
final AllowedSwipeDirection cardSwipeDirection;
|
||||
|
||||
double left = 0;
|
||||
double top = 0;
|
||||
|
|
@ -53,12 +59,28 @@ class CardAnimation {
|
|||
}
|
||||
|
||||
void update(double dx, double dy, bool inverseAngle) {
|
||||
//TODO: remove [isHorizontalSwipingEnabled] checks in the next major release
|
||||
if (isHorizontalSwipingEnabled) {
|
||||
left += dx;
|
||||
if (cardSwipeDirection.right && cardSwipeDirection.left) {
|
||||
left += dx;
|
||||
} else if (cardSwipeDirection.right) {
|
||||
if (left >= 0) left += dx;
|
||||
} else if (cardSwipeDirection.left) {
|
||||
if (left <= 0) left += dx;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: remove [isHorizontalSwipingEnabled] checks in the next major release
|
||||
if (isVerticalSwipingEnabled) {
|
||||
top += dy;
|
||||
if (cardSwipeDirection.up && cardSwipeDirection.down) {
|
||||
top += dy;
|
||||
} else if (cardSwipeDirection.up) {
|
||||
if (top <= 0) top += dy;
|
||||
} else if (cardSwipeDirection.down) {
|
||||
if (top >= 0) top += dy;
|
||||
}
|
||||
}
|
||||
|
||||
total = left + top;
|
||||
updateAngle(inverseAngle);
|
||||
updateScale();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
/// Class to define the direction in which the card can be swiped
|
||||
class AllowedSwipeDirection {
|
||||
/// Set to true to allow the card to be swiped in the up direction
|
||||
final bool up;
|
||||
|
||||
/// Set to true to allow the card to be swiped in the down direction
|
||||
final bool down;
|
||||
|
||||
/// Set to true to allow the card to be swiped in the left direction
|
||||
final bool left;
|
||||
|
||||
/// Set to true to allow the card to be swiped in the right direction
|
||||
final bool right;
|
||||
|
||||
/// Define the direction in which the card can be swiped
|
||||
const AllowedSwipeDirection._({
|
||||
required this.up,
|
||||
required this.down,
|
||||
required this.left,
|
||||
required this.right,
|
||||
});
|
||||
|
||||
/// Allow the card to be swiped in any direction
|
||||
const AllowedSwipeDirection.all()
|
||||
: up = true,
|
||||
down = true,
|
||||
right = true,
|
||||
left = true;
|
||||
|
||||
/// Does not allow the card to be swiped in any direction
|
||||
const AllowedSwipeDirection.none()
|
||||
: up = false,
|
||||
down = false,
|
||||
right = false,
|
||||
left = false;
|
||||
|
||||
/// Allow the card to be swiped in only the specified directions
|
||||
factory AllowedSwipeDirection.only({
|
||||
up = false,
|
||||
down = false,
|
||||
left = false,
|
||||
right = false,
|
||||
}) =>
|
||||
AllowedSwipeDirection._(
|
||||
up: up,
|
||||
down: down,
|
||||
left: left,
|
||||
right: right,
|
||||
);
|
||||
|
||||
/// Allow the card to be swiped in symmetrically in horizontal or vertical directions
|
||||
factory AllowedSwipeDirection.symmetric({
|
||||
horizontal = false,
|
||||
vertical = false,
|
||||
}) =>
|
||||
AllowedSwipeDirection._(
|
||||
up: vertical,
|
||||
down: vertical,
|
||||
right: horizontal,
|
||||
left: horizontal,
|
||||
);
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import 'dart:math' as math;
|
|||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_card_swiper/src/card_animation.dart';
|
||||
import 'package:flutter_card_swiper/src/card_swipe_direction.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/extensions.dart';
|
||||
|
|
@ -92,11 +93,21 @@ class CardSwiper extends StatefulWidget {
|
|||
final CardSwiperDirection direction;
|
||||
|
||||
/// A boolean value that determines whether the card can be swiped horizontally. The default value is true.
|
||||
@Deprecated(
|
||||
'Will be deprecated in the next major release. Use [AllowedSwipeDirection] instead',
|
||||
)
|
||||
final bool isHorizontalSwipingEnabled;
|
||||
|
||||
/// A boolean value that determines whether the card can be swiped vertically. The default value is true.
|
||||
@Deprecated(
|
||||
'Will be deprecated in the next major release. Use [AllowedSwipeDirection] instead',
|
||||
)
|
||||
final bool isVerticalSwipingEnabled;
|
||||
|
||||
/// Defined the directions in which the card is allowed to be swiped.
|
||||
/// Defaults to [AllowedSwipeDirection.all]
|
||||
final AllowedSwipeDirection allowedSwipeDirection;
|
||||
|
||||
/// A boolean value that determines whether the card stack should loop. When the last card is swiped,
|
||||
/// if isLoop is true, the first card will become the last card again. The default value is true.
|
||||
final bool isLoop;
|
||||
|
|
@ -138,8 +149,11 @@ class CardSwiper extends StatefulWidget {
|
|||
this.onSwipe,
|
||||
this.onEnd,
|
||||
this.direction = CardSwiperDirection.right,
|
||||
this.isHorizontalSwipingEnabled = true,
|
||||
this.isVerticalSwipingEnabled = true,
|
||||
@Deprecated('Will be deprecated in the next major release. Use [allowedSwipeDirection] instead')
|
||||
this.isHorizontalSwipingEnabled = true,
|
||||
@Deprecated('Will be deprecated in the next major release. Use [allowedSwipeDirection] instead')
|
||||
this.isVerticalSwipingEnabled = true,
|
||||
this.allowedSwipeDirection = const AllowedSwipeDirection.all(),
|
||||
this.isLoop = true,
|
||||
this.numberOfCardsDisplayed = 2,
|
||||
this.onUndo,
|
||||
|
|
@ -187,7 +201,9 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
|||
final Queue<CardSwiperDirection> _directionHistory = Queue();
|
||||
|
||||
int? get _currentIndex => _undoableIndex.state;
|
||||
|
||||
int? get _nextIndex => getValidIndexOffset(1);
|
||||
|
||||
bool get _canSwipe => _currentIndex != null && !widget.isDisabled;
|
||||
|
||||
@override
|
||||
|
|
@ -211,6 +227,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
|||
initialScale: widget.scale,
|
||||
isVerticalSwipingEnabled: widget.isVerticalSwipingEnabled,
|
||||
isHorizontalSwipingEnabled: widget.isHorizontalSwipingEnabled,
|
||||
cardSwipeDirection: widget.allowedSwipeDirection,
|
||||
initialOffset: widget.backCardOffset,
|
||||
);
|
||||
}
|
||||
|
|
@ -376,12 +393,26 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
|
|||
final direction = _cardAnimation.left.isNegative
|
||||
? CardSwiperDirection.left
|
||||
: CardSwiperDirection.right;
|
||||
_swipe(direction);
|
||||
if (direction == CardSwiperDirection.left &&
|
||||
widget.allowedSwipeDirection.left ||
|
||||
direction == CardSwiperDirection.right &&
|
||||
widget.allowedSwipeDirection.right) {
|
||||
_swipe(direction);
|
||||
} else {
|
||||
_goBack();
|
||||
}
|
||||
} else if (_cardAnimation.top.abs() > widget.threshold) {
|
||||
final direction = _cardAnimation.top.isNegative
|
||||
? CardSwiperDirection.top
|
||||
: CardSwiperDirection.bottom;
|
||||
_swipe(direction);
|
||||
if (direction == CardSwiperDirection.top &&
|
||||
widget.allowedSwipeDirection.up ||
|
||||
direction == CardSwiperDirection.bottom &&
|
||||
widget.allowedSwipeDirection.down) {
|
||||
_swipe(direction);
|
||||
} else {
|
||||
_goBack();
|
||||
}
|
||||
} else {
|
||||
_goBack();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
import 'package:flutter_card_swiper/flutter_card_swiper.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
test('CardSwipeDirection.all() has all directions as true', () {
|
||||
const directions = AllowedSwipeDirection.all();
|
||||
expect(directions.up, true);
|
||||
expect(directions.down, true);
|
||||
expect(directions.right, true);
|
||||
expect(directions.left, true);
|
||||
});
|
||||
|
||||
test('CardSwipeDirection.none() has all directions as false', () {
|
||||
const directions = AllowedSwipeDirection.none();
|
||||
expect(directions.up, false);
|
||||
expect(directions.down, false);
|
||||
expect(directions.right, false);
|
||||
expect(directions.left, false);
|
||||
});
|
||||
|
||||
group('CardSwipeDirection.only() tests', () {
|
||||
test('CardSwipeDirection.only(up:true) has only the up direction as true',
|
||||
() {
|
||||
final directions = AllowedSwipeDirection.only(up: true);
|
||||
expect(directions.up, true);
|
||||
expect(directions.down, false);
|
||||
expect(directions.right, false);
|
||||
expect(directions.left, false);
|
||||
});
|
||||
|
||||
test(
|
||||
'CardSwipeDirection.only(down:true) has only the set direction as true',
|
||||
() {
|
||||
final directions = AllowedSwipeDirection.only(down: true);
|
||||
expect(directions.up, false);
|
||||
expect(directions.down, true);
|
||||
expect(directions.right, false);
|
||||
expect(directions.left, false);
|
||||
});
|
||||
|
||||
test(
|
||||
'CardSwipeDirection.only(right:true) has only the set direction as true',
|
||||
() {
|
||||
final directions = AllowedSwipeDirection.only(right: true);
|
||||
expect(directions.up, false);
|
||||
expect(directions.down, false);
|
||||
expect(directions.right, true);
|
||||
expect(directions.left, false);
|
||||
});
|
||||
|
||||
test(
|
||||
'CardSwipeDirection.only(left:true) has only the set direction as true',
|
||||
() {
|
||||
final directions = AllowedSwipeDirection.only(left: true);
|
||||
expect(directions.up, false);
|
||||
expect(directions.down, false);
|
||||
expect(directions.right, false);
|
||||
expect(directions.left, true);
|
||||
});
|
||||
});
|
||||
|
||||
group('CardSwipeDirection.symmetric() tests', () {
|
||||
test(
|
||||
'CardSwipeDirection.symmetric(horizontal:true) has left and right as true',
|
||||
() {
|
||||
final directions = AllowedSwipeDirection.symmetric(horizontal: true);
|
||||
expect(directions.up, false);
|
||||
expect(directions.down, false);
|
||||
expect(directions.right, true);
|
||||
expect(directions.left, true);
|
||||
});
|
||||
|
||||
test('CardSwipeDirection.symmetric(vertical:true) has up and down as true',
|
||||
() {
|
||||
final directions = AllowedSwipeDirection.symmetric(vertical: true);
|
||||
expect(directions.up, true);
|
||||
expect(directions.down, true);
|
||||
expect(directions.right, false);
|
||||
expect(directions.left, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -465,4 +465,174 @@ void main() {
|
|||
expect(isCalled, true);
|
||||
});
|
||||
});
|
||||
|
||||
group('Card swipe direction tests', () {
|
||||
testWidgets(
|
||||
'when swiping right and AllowedSwipeDirection.right=true,'
|
||||
' expect to see the next card', (WidgetTester tester) async {
|
||||
final swiperKey = GlobalKey();
|
||||
|
||||
await tester.pumpApp(
|
||||
CardSwiper(
|
||||
key: swiperKey,
|
||||
cardsCount: 10,
|
||||
numberOfCardsDisplayed: 1,
|
||||
allowedSwipeDirection: AllowedSwipeDirection.only(right: true),
|
||||
cardBuilder: genericBuilder,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.dragRight(swiperKey);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.card(1), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'when swiping right and AllowedSwipeDirection.right=false,'
|
||||
' expect to see the same card', (WidgetTester tester) async {
|
||||
final swiperKey = GlobalKey();
|
||||
|
||||
await tester.pumpApp(
|
||||
CardSwiper(
|
||||
key: swiperKey,
|
||||
cardsCount: 10,
|
||||
numberOfCardsDisplayed: 1,
|
||||
allowedSwipeDirection: AllowedSwipeDirection.only(left: true),
|
||||
cardBuilder: genericBuilder,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.dragRight(swiperKey);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.card(0), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'when swiping left and AllowedSwipeDirection.left=true,'
|
||||
' expect to see the next card', (WidgetTester tester) async {
|
||||
final swiperKey = GlobalKey();
|
||||
|
||||
await tester.pumpApp(
|
||||
CardSwiper(
|
||||
key: swiperKey,
|
||||
cardsCount: 10,
|
||||
numberOfCardsDisplayed: 1,
|
||||
allowedSwipeDirection: AllowedSwipeDirection.only(left: true),
|
||||
cardBuilder: genericBuilder,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.dragLeft(swiperKey);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.card(1), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'when swiping left and AllowedSwipeDirection.left=false,'
|
||||
' expect to see the same card', (WidgetTester tester) async {
|
||||
final swiperKey = GlobalKey();
|
||||
|
||||
await tester.pumpApp(
|
||||
CardSwiper(
|
||||
key: swiperKey,
|
||||
cardsCount: 10,
|
||||
numberOfCardsDisplayed: 1,
|
||||
allowedSwipeDirection: AllowedSwipeDirection.only(right: true),
|
||||
cardBuilder: genericBuilder,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.dragLeft(swiperKey);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.card(0), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'when swiping up and AllowedSwipeDirection.up=true,'
|
||||
' expect to see the next card', (WidgetTester tester) async {
|
||||
final swiperKey = GlobalKey();
|
||||
|
||||
await tester.pumpApp(
|
||||
CardSwiper(
|
||||
key: swiperKey,
|
||||
cardsCount: 10,
|
||||
numberOfCardsDisplayed: 1,
|
||||
allowedSwipeDirection: AllowedSwipeDirection.only(up: true),
|
||||
cardBuilder: genericBuilder,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.dragUp(swiperKey);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.card(1), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'when swiping up and AllowedSwipeDirection.up=false,'
|
||||
' expect to see the same card', (WidgetTester tester) async {
|
||||
final swiperKey = GlobalKey();
|
||||
|
||||
await tester.pumpApp(
|
||||
CardSwiper(
|
||||
key: swiperKey,
|
||||
cardsCount: 10,
|
||||
numberOfCardsDisplayed: 1,
|
||||
allowedSwipeDirection: AllowedSwipeDirection.only(down: true),
|
||||
cardBuilder: genericBuilder,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.dragUp(swiperKey);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.card(0), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'when swiping down and AllowedSwipeDirection.down=true,'
|
||||
' expect to see the next card', (WidgetTester tester) async {
|
||||
final swiperKey = GlobalKey();
|
||||
|
||||
await tester.pumpApp(
|
||||
CardSwiper(
|
||||
key: swiperKey,
|
||||
cardsCount: 10,
|
||||
numberOfCardsDisplayed: 1,
|
||||
allowedSwipeDirection: AllowedSwipeDirection.only(down: true),
|
||||
cardBuilder: genericBuilder,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.dragDown(swiperKey);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.card(1), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'when swiping down and AllowedSwipeDirection.down=false,'
|
||||
' expect to see the same card', (WidgetTester tester) async {
|
||||
final swiperKey = GlobalKey();
|
||||
|
||||
await tester.pumpApp(
|
||||
CardSwiper(
|
||||
key: swiperKey,
|
||||
cardsCount: 10,
|
||||
numberOfCardsDisplayed: 1,
|
||||
allowedSwipeDirection: AllowedSwipeDirection.only(up: true),
|
||||
cardBuilder: genericBuilder,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.dragDown(swiperKey);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.card(0), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue