Skip to content

Commit

Permalink
feat: additional info to cardBuilder (#22)
Browse files Browse the repository at this point in the history
Additional swiping info to cardBuilder callback
Add an additional callback containing the horizontal and vertical swipe direction 

Co-authored-by: samuel.peirson.knowunity <[email protected]>
  • Loading branch information
sarope and samuel.peirson.knowunity authored Jun 26, 2023
1 parent f1ab70f commit 1f23f08
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 21 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## NEXT

## [6.0.0]

- Adds `onSwipeDirectionChange` callback containing the horizontal and vertical swipe direction
- **BREAKING CHANGE**:
- Modifies the `cardBuilder` callback, to include the ratio of horizontal drag to threshold as a percentage
and the ratio of vertical drag to threshold as a percentage.
- **BREAKING CHANGE**:
- `isHorizontalSwipingEnabled` and `isVerticalSwipingEnabled` have been removed. Use `allowedSwipeDirection` instead.

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class Example extends StatelessWidget {
body: Flexible(
child: CardSwiper(
cardsCount: cards.length,
cardBuilder: (context, index) => cards[index],
cardBuilder: (context, index, percentThresholdX, percentThresholdY) => cards[index],
),
),
);
Expand All @@ -97,7 +97,7 @@ class Example extends StatelessWidget {
#### Basic

| Parameter | Default | Description | Required |
|-------------------------------------------| :------------------------------------------------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------| :------: |
|-------------------------------------------|:---------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------| :------: |
| cardBuilder | - | Widget builder for rendering cards | true |
| cardsCount | - | Number of cards | true |
| controller | - | Controller to trigger swipe actions | false |
Expand All @@ -116,7 +116,7 @@ class Example extends StatelessWidget {
| padding | EdgeInsets.symmetric(horizontal: 20, vertical: 25) | The padding around the swiper | false |
| scale | 0.9 | Scale of the card that is behind the front card | false |
| threshold | 50 | Threshold from which the card is swiped away | false |

| onSwipeDirectionChange | - | A callback containing the horizontal and vertical swipe direction | false |
#### Controller

The `Controller` is used to swipe the card from outside of the widget. You can create a controller called `CardSwiperController` and save the instance for further usage. Please have a closer look at our [Example](https://github.com/ricardodalarme/flutter_card_swiper/tree/main/example) for the usage.
Expand Down
8 changes: 7 additions & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ class _ExamplePageState extends State<Example> {
numberOfCardsDisplayed: 3,
backCardOffset: const Offset(40, 40),
padding: const EdgeInsets.all(24.0),
cardBuilder: (context, index) => cards[index],
cardBuilder: (
context,
index,
horizontalThresholdPercentage,
verticalThresholdPercentage,
) =>
cards[index],
),
),
Padding(
Expand Down
32 changes: 28 additions & 4 deletions lib/src/card_animation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class CardAnimation {
this.isHorizontalSwipingEnabled = true,
this.isVerticalSwipingEnabled = true,
this.allowedSwipeDirection = const AllowedSwipeDirection.all(),
this.onSwipeDirectionChanged,
}) : scale = initialScale;

final double maxAngle;
Expand All @@ -22,6 +23,7 @@ class CardAnimation {
final bool isHorizontalSwipingEnabled;
final bool isVerticalSwipingEnabled;
final AllowedSwipeDirection allowedSwipeDirection;
final ValueChanged<CardSwiperDirection>? onSwipeDirectionChanged;

double left = 0;
double top = 0;
Expand Down Expand Up @@ -56,19 +58,41 @@ class CardAnimation {

void update(double dx, double dy, bool inverseAngle) {
if (allowedSwipeDirection.right && allowedSwipeDirection.left) {
if (left > 0) {
onSwipeDirectionChanged?.call(CardSwiperDirection.right);
} else if (left < 0) {
onSwipeDirectionChanged?.call(CardSwiperDirection.left);
}
left += dx;
} else if (allowedSwipeDirection.right) {
if (left >= 0) left += dx;
if (left >= 0) {
onSwipeDirectionChanged?.call(CardSwiperDirection.right);
left += dx;
}
} else if (allowedSwipeDirection.left) {
if (left <= 0) left += dx;
if (left <= 0) {
onSwipeDirectionChanged?.call(CardSwiperDirection.left);
left += dx;
}
}

if (allowedSwipeDirection.up && allowedSwipeDirection.down) {
if (top > 0) {
onSwipeDirectionChanged?.call(CardSwiperDirection.bottom);
} else if (top < 0) {
onSwipeDirectionChanged?.call(CardSwiperDirection.top);
}
top += dy;
} else if (allowedSwipeDirection.up) {
if (top <= 0) top += dy;
if (top <= 0) {
onSwipeDirectionChanged?.call(CardSwiperDirection.top);
top += dy;
}
} else if (allowedSwipeDirection.down) {
if (top >= 0) top += dy;
if (top >= 0) {
onSwipeDirectionChanged?.call(CardSwiperDirection.bottom);
top += dy;
}
}

total = left + top;
Expand Down
56 changes: 46 additions & 10 deletions lib/src/card_swiper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import 'package:flutter_card_swiper/src/undoable.dart';
class CardSwiper extends StatefulWidget {
/// Function that builds each card in the stack.
///
/// The [int] parameter specifies the index of the card to build, and the [BuildContext]
/// parameter provides the build context. The function should return a widget that represents
/// the card at the given index. It can return `null`, which will result in an
/// empty card being displayed.
final NullableIndexedWidgetBuilder cardBuilder;
/// The function is called with the index of the card to be built, the build context, the ratio
/// of vertical drag to [threshold] as a percentage, and the ratio of horizontal drag to [threshold]
/// as a percentage. The function should return a widget that represents the card at the given index.
/// It can return `null`, which will result in an empty card being displayed.
final NullableCardBuilder cardBuilder;

/// The number of cards in the stack.
///
Expand Down Expand Up @@ -111,6 +111,11 @@ class CardSwiper extends StatefulWidget {
/// on top of the stack. If the function returns `true`, the undo action is performed as expected.
final CardSwiperOnUndo? onUndo;

/// Callback function that is called when a card swipe direction changes.
///
/// The function is called with the last detected horizontal direction and the last detected vertical direction
final CardSwiperDirectionChange? onSwipeDirectionChange;

/// The offset of the back card from the front card.
///
/// In order to keep the back card position same after changing the [backCardOffset],
Expand All @@ -137,6 +142,7 @@ class CardSwiper extends StatefulWidget {
this.onSwipe,
this.onEnd,
this.direction = CardSwiperDirection.right,
this.onSwipeDirectionChange,
this.allowedSwipeDirection = const AllowedSwipeDirection.all(),
this.isLoop = true,
this.numberOfCardsDisplayed = 2,
Expand Down Expand Up @@ -179,6 +185,8 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>

SwipeType _swipeType = SwipeType.none;
CardSwiperDirection _detectedDirection = CardSwiperDirection.none;
CardSwiperDirection _detectedHorizontalDirection = CardSwiperDirection.none;
CardSwiperDirection _detectedVerticalDirection = CardSwiperDirection.none;
bool _tappedOnTop = false;

final _undoableIndex = Undoable<int?>(null);
Expand Down Expand Up @@ -211,9 +219,33 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
initialScale: widget.scale,
allowedSwipeDirection: widget.allowedSwipeDirection,
initialOffset: widget.backCardOffset,
onSwipeDirectionChanged: onSwipeDirectionChanged,
);
}

void onSwipeDirectionChanged(CardSwiperDirection direction) {
if (direction == CardSwiperDirection.none) {
_detectedVerticalDirection = direction;
_detectedHorizontalDirection = direction;
widget.onSwipeDirectionChange
?.call(_detectedHorizontalDirection, _detectedVerticalDirection);
} else if (direction == CardSwiperDirection.right ||
direction == CardSwiperDirection.left) {
if (_detectedHorizontalDirection != direction) {
_detectedHorizontalDirection = direction;
widget.onSwipeDirectionChange
?.call(_detectedHorizontalDirection, _detectedVerticalDirection);
}
} else if (direction == CardSwiperDirection.top ||
direction == CardSwiperDirection.bottom) {
if (_detectedVerticalDirection != direction) {
_detectedVerticalDirection = direction;
widget.onSwipeDirectionChange
?.call(_detectedHorizontalDirection, _detectedVerticalDirection);
}
}
}

@override
void dispose() {
super.dispose();
Expand All @@ -234,7 +266,6 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
fit: StackFit.expand,
children: List.generate(numberOfCardsOnScreen(), (index) {
if (index == 0) return _frontItem(constraints);

return _backItem(constraints, index);
}).reversed.toList(),
);
Expand All @@ -254,7 +285,12 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
angle: _cardAnimation.angle,
child: ConstrainedBox(
constraints: constraints,
child: widget.cardBuilder(context, _currentIndex!),
child: widget.cardBuilder(
context,
_currentIndex!,
(100 * _cardAnimation.left / widget.threshold).ceil(),
(100 * _cardAnimation.top / widget.threshold).ceil(),
),
),
),
onTap: () async {
Expand Down Expand Up @@ -299,7 +335,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
scale: _cardAnimation.scale - ((1 - widget.scale) * (index - 1)),
child: ConstrainedBox(
constraints: constraints,
child: widget.cardBuilder(context, getValidIndexOffset(index)!),
child: widget.cardBuilder(context, getValidIndexOffset(index)!, 0, 0),
),
),
);
Expand Down Expand Up @@ -363,6 +399,8 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
}

void _reset() {
onSwipeDirectionChanged(CardSwiperDirection.none);
_detectedDirection = CardSwiperDirection.none;
setState(() {
_animationController.reset();
_cardAnimation.reset();
Expand Down Expand Up @@ -402,15 +440,13 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>

void _swipe(CardSwiperDirection direction) {
if (_currentIndex == null) return;

_swipeType = SwipeType.swipe;
_detectedDirection = direction;
_cardAnimation.animate(context, direction);
}

void _goBack() {
_swipeType = SwipeType.back;
_detectedDirection = CardSwiperDirection.none;
_cardAnimation.animateBack(context);
}

Expand Down
18 changes: 18 additions & 0 deletions lib/src/typedefs.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_card_swiper/src/enums.dart';

typedef CardSwiperOnSwipe = FutureOr<bool> Function(
Expand All @@ -8,6 +9,23 @@ typedef CardSwiperOnSwipe = FutureOr<bool> Function(
CardSwiperDirection direction,
);

typedef CardSwiperOnSwipeUpdate = Function(
int? currentIndex,
CardSwiperDirection direction,
);

typedef NullableCardBuilder = Widget? Function(
BuildContext context,
int index,
int horizontalOffsetPercentage,
int verticalOffsetPercentage,
);

typedef CardSwiperDirectionChange = Function(
CardSwiperDirection horizontalDirection,
CardSwiperDirection verticalDirection,
);

typedef CardSwiperOnEnd = FutureOr<void> Function();

typedef CardSwiperOnTapDisabled = FutureOr<void> Function();
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
homepage: https://github.com/ricardodalarme/flutter_card_swiper
issue_tracker: https://github.com/ricardodalarme/flutter_card_swiper/issues
version: 5.1.0
version: 6.0.0

environment:
sdk: ">=2.12.0 <4.0.0"
Expand Down
7 changes: 6 additions & 1 deletion test/test_helpers/card_builder.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import 'package:flutter/material.dart';

Widget? genericBuilder(BuildContext context, int index) {
Widget? genericBuilder(
BuildContext context,
int index,
int horizontalPercentage,
int verticalPercentage,
) {
return Container(
width: 200,
height: 200,
Expand Down

0 comments on commit 1f23f08

Please sign in to comment.