Compare commits

..

1 Commits

Author SHA1 Message Date
JohnE bbf19cd498 WIP: tap card 2024-10-14 19:47:52 -07:00
8 changed files with 23 additions and 77 deletions

View File

@ -1,8 +1,3 @@
## [7.1.0]
- Adds support for tapping a card
- `onTap` callback containing currentIndex.
## [7.0.2] ## [7.0.2]
- Added `CardAnimation.animateToAngle` helper to animate swipe the card to any given angle between 0-360°. - Added `CardAnimation.animateToAngle` helper to animate swipe the card to any given angle between 0-360°.

View File

@ -15,36 +15,16 @@ class CardSwiperController {
_eventController.add(ControllerSwipeEvent(direction)); _eventController.add(ControllerSwipeEvent(direction));
} }
/// Undo the last swipe // Undo the last swipe
void undo() { void undo() {
_eventController.add(const ControllerUndoEvent()); _eventController.add(const ControllerUndoEvent());
} }
/// Change the top card to a specific index. // Change the top card to a specific index.
void moveTo(int index) { void moveTo(int index) {
_eventController.add(ControllerMoveEvent(index)); _eventController.add(ControllerMoveEvent(index));
} }
/// We have a new list of card data for this state machine to handle
/// 1. Set the length of the card list
/// 2. Move index to 0 (restart stack to first card)
void newStack(int length) {
_eventController.add(ControllerNewStackEvent(length));
}
/// We have a new added cards to the end
/// 0. Keep internal index the same (undo will work, can go backwards)
/// 1. Set the internal length of the card list
/// 2. Check if we need to redraw, if we are at end of card list
// void appendStack(int length) {
// _eventController.add(ControllerNewStackEvent(length));
// }
/// Set the length only, user can moveTo(0), or other actions
// void modifiedStack(int length) {
// _eventController.add(ControllerNewStackEvent(length));
// }
Future<void> dispose() async { Future<void> dispose() async {
await _eventController.close(); await _eventController.close();
} }

View File

@ -17,8 +17,3 @@ class ControllerMoveEvent extends ControllerEvent {
final int index; final int index;
const ControllerMoveEvent(this.index); const ControllerMoveEvent(this.index);
} }
class ControllerNewStackEvent extends ControllerEvent {
final int length;
const ControllerNewStackEvent(this.length);
}

View File

@ -36,4 +36,4 @@ typedef CardSwiperOnUndo = bool Function(
CardSwiperDirection direction, CardSwiperDirection direction,
); );
typedef CardSwiperOnTap = FutureOr<void> Function(int currentIndex); typedef CardSwiperOnTap = FutureOr<void> Function();

View File

@ -159,10 +159,10 @@ class CardSwiper extends StatefulWidget {
scale >= 0 && scale <= 1, scale >= 0 && scale <= 1,
'scale must be between 0 and 1', 'scale must be between 0 and 1',
), ),
// assert( assert(
// numberOfCardsDisplayed >= 1 && numberOfCardsDisplayed <= cardsCount, numberOfCardsDisplayed >= 1 && numberOfCardsDisplayed <= cardsCount,
// 'you must display at least one card, and no more than [cardsCount]', 'you must display at least one card, and no more than [cardsCount]',
// ), ),
assert( assert(
initialIndex >= 0 && initialIndex < cardsCount, initialIndex >= 0 && initialIndex < cardsCount,
'initialIndex must be between 0 and [cardsCount]', 'initialIndex must be between 0 and [cardsCount]',

View File

@ -22,20 +22,10 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
StreamSubscription<ControllerEvent>? controllerSubscription; StreamSubscription<ControllerEvent>? controllerSubscription;
late NullableCardBuilder _cardBuilder;
late int _cardsCount;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
debugPrint('[WTF] _CardSwiperState.initState !!!');
debugPrint('[WTF] _CardSwiperState.initState !!!');
debugPrint('[WTF] _CardSwiperState.initState !!!');
_cardBuilder = widget.cardBuilder;
_cardsCount = widget.cardsCount;
_undoableIndex.state = widget.initialIndex; _undoableIndex.state = widget.initialIndex;
controllerSubscription = controllerSubscription =
@ -77,11 +67,6 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
@override @override
void dispose() { void dispose() {
debugPrint('[WTF] _CardSwiperState.dispose !!!');
debugPrint('[WTF] _CardSwiperState.dispose !!!');
debugPrint('[WTF] _CardSwiperState.dispose !!!');
_animationController.dispose(); _animationController.dispose();
controllerSubscription?.cancel(); controllerSubscription?.cancel();
super.dispose(); super.dispose();
@ -119,7 +104,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
angle: _cardAnimation.angle, angle: _cardAnimation.angle,
child: ConstrainedBox( child: ConstrainedBox(
constraints: constraints, constraints: constraints,
child: _cardBuilder( child: widget.cardBuilder(
context, context,
_currentIndex!, _currentIndex!,
(100 * _cardAnimation.left / widget.threshold).ceil(), (100 * _cardAnimation.left / widget.threshold).ceil(),
@ -131,7 +116,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
if (widget.isDisabled) { if (widget.isDisabled) {
await widget.onTapDisabled?.call(); await widget.onTapDisabled?.call();
} }
await widget.onTap?.call(_currentIndex!); await widget.onTap?.call();
}, },
onPanStart: (tapInfo) { onPanStart: (tapInfo) {
if (!widget.isDisabled) { if (!widget.isDisabled) {
@ -170,7 +155,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
scale: _cardAnimation.scale - ((1 - widget.scale) * (index - 1)), scale: _cardAnimation.scale - ((1 - widget.scale) * (index - 1)),
child: ConstrainedBox( child: ConstrainedBox(
constraints: constraints, constraints: constraints,
child: _cardBuilder(context, getValidIndexOffset(index)!, 0, 0), child: widget.cardBuilder(context, getValidIndexOffset(index)!, 0, 0),
), ),
), ),
); );
@ -181,8 +166,6 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
ControllerSwipeEvent(:final direction) => _swipe(direction), ControllerSwipeEvent(:final direction) => _swipe(direction),
ControllerUndoEvent() => _undo(), ControllerUndoEvent() => _undo(),
ControllerMoveEvent(:final index) => _moveTo(index), ControllerMoveEvent(:final index) => _moveTo(index),
// controller
ControllerNewStackEvent(:final length) => _newCardStack(length)
}; };
} }
@ -206,7 +189,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
} }
Future<void> _handleCompleteSwipe() async { Future<void> _handleCompleteSwipe() async {
final isLastCard = _currentIndex! == _cardsCount - 1; final isLastCard = _currentIndex! == widget.cardsCount - 1;
final shouldCancelSwipe = await widget.onSwipe final shouldCancelSwipe = await widget.onSwipe
?.call(_currentIndex!, _nextIndex, _detectedDirection) == ?.call(_currentIndex!, _nextIndex, _detectedDirection) ==
false; false;
@ -303,20 +286,14 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
} }
void _moveTo(int index) { void _moveTo(int index) {
debugPrint('[WTF] _moveTo() index: $index currentIndex: $_currentIndex cardsCount: $_cardsCount');
if (index == _currentIndex) return; if (index == _currentIndex) return;
if (index < 0 || index >= _cardsCount) return; if (index < 0 || index >= widget.cardsCount) return;
setState(() { setState(() {
_undoableIndex.state = index; _undoableIndex.state = index;
}); });
} }
void _newCardStack(int length) {
_cardsCount = length;
_moveTo(0); // move to first card, and force redraw if necessary
}
int numberOfCardsOnScreen() { int numberOfCardsOnScreen() {
if (widget.isLoop) { if (widget.isLoop) {
return widget.numberOfCardsDisplayed; return widget.numberOfCardsDisplayed;
@ -327,7 +304,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
return math.min( return math.min(
widget.numberOfCardsDisplayed, widget.numberOfCardsDisplayed,
_cardsCount - _currentIndex!, widget.cardsCount - _currentIndex!,
); );
} }
@ -337,9 +314,9 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
} }
final index = _currentIndex! + offset; final index = _currentIndex! + offset;
if (!widget.isLoop && !index.isBetween(0, _cardsCount - 1)) { if (!widget.isLoop && !index.isBetween(0, widget.cardsCount - 1)) {
return null; return null;
} }
return index % _cardsCount; return index % widget.cardsCount;
} }
} }

View File

@ -2,7 +2,7 @@ name: flutter_card_swiper
description: This is a Tinder-like card swiper package. It allows you to swipe left, right, up, and down and define your own business logic for each direction. description: This is a Tinder-like card swiper package. It allows you to swipe left, right, up, and down and define your own business logic for each direction.
homepage: https://github.com/ricardodalarme/flutter_card_swiper homepage: https://github.com/ricardodalarme/flutter_card_swiper
issue_tracker: https://github.com/ricardodalarme/flutter_card_swiper/issues issue_tracker: https://github.com/ricardodalarme/flutter_card_swiper/issues
version: 7.1.0 version: 7.0.2
environment: environment:
sdk: ">=3.0.0 <4.0.0" sdk: ">=3.0.0 <4.0.0"

View File

@ -635,28 +635,27 @@ void main() {
expect(find.card(0), findsOneWidget); expect(find.card(0), findsOneWidget);
}); });
testWidgets('when tapping a card, expect callback', testWidgets('when tapping a card, expect callback', (WidgetTester tester) async {
(WidgetTester tester) async {
final swiperKey = GlobalKey(); final swiperKey = GlobalKey();
var index = -1; var isCalled = false;
await tester.pumpApp( await tester.pumpApp(
CardSwiper( CardSwiper(
key: swiperKey, key: swiperKey,
cardsCount: 3, cardsCount: 3,
numberOfCardsDisplayed: 1, numberOfCardsDisplayed: 1,
initialIndex: 2, onTap: () {
onTap: (currentIndex) { isCalled = true;
index = currentIndex;
}, },
cardBuilder: genericBuilder, cardBuilder: genericBuilder,
), ),
); );
await tester.tap(find.byKey(swiperKey)); await tester.dragDown(swiperKey);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(index, 2); expect(isCalled, true);
}); });
}); });
} }