diff --git a/README.md b/README.md
index f027fc3..1bf089b 100644
--- a/README.md
+++ b/README.md
@@ -44,6 +44,11 @@ SwipeCards(
| `rightSwipeAllowed` | To enable/disable right swipe. (Default: true) |
| `upSwipeAllowed` | To enable/disable up swipe. (Default: false) |
| `fillSpace` | Config weather to fill up the space or not. (Default: true) |
+| `leftSwipeThreshold`| Threshold for left swipe. The larger the number, the further the card needs to be dragged to activate the action. Must be less or equal to 0 to work correct. (Default: -0.45) |
+| `rightSwipeThreshold`| Threshold for right swipe. The larger the number, the further the card needs to be dragged to activate the action. Must be bigger or equal to 0 to work correct. (Default: 0.45) |
+| `topSwipeThreshold`| Threshold for top swipe. The larger the number, the further the card needs to be dragged to activate the action. (Default: -0.4) |
+| `onSwipeChange` | Triggered when user dragging the card |
+| `onSwipeFinish` | Triggered when user releases the card |
### MatchEngine
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index bb9a61d..13ad1a5 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 50;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -171,10 +171,12 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
@@ -185,6 +187,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist
index 1579fb3..4f68a2c 100644
--- a/example/ios/Runner/Info.plist
+++ b/example/ios/Runner/Info.plist
@@ -43,5 +43,7 @@
CADisableMinimumFrameDurationOnPhone
+ UIApplicationSupportsIndirectInputEvents
+
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 5818977..8310283 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -35,16 +35,7 @@ class _MyHomePageState extends State {
List _swipeItems = [];
MatchEngine? _matchEngine;
GlobalKey _scaffoldKey = GlobalKey();
- List _names = [
- "Red",
- "Blue",
- "Green",
- "Yellow",
- "Orange",
- "Grey",
- "Purple",
- "Pink"
- ];
+ List _names = ["Red", "Blue", "Green", "Yellow", "Orange", "Grey", "Purple", "Pink"];
List _colors = [
Colors.red,
Colors.blue,
@@ -120,6 +111,14 @@ class _MyHomePageState extends State {
itemChanged: (SwipeItem item, int index) {
print("item: ${item.content.text}, index: $index");
},
+ onSwipeChange: (item, slideRegion) {
+ print('item: ${item.content.text} onSwipeChange: $slideRegion');
+ },
+ onSwipeFinish: (item, slideRegion) {
+ print('item: ${item.content.text} onSwipeFinish: $slideRegion');
+ },
+ leftSwipeThreshold: -0.3,
+ rightSwipeThreshold: 0.3,
leftSwipeAllowed: true,
rightSwipeAllowed: true,
upSwipeAllowed: true,
@@ -127,25 +126,19 @@ class _MyHomePageState extends State {
likeTag: Container(
margin: const EdgeInsets.all(15.0),
padding: const EdgeInsets.all(3.0),
- decoration: BoxDecoration(
- border: Border.all(color: Colors.green)
- ),
+ decoration: BoxDecoration(border: Border.all(color: Colors.green)),
child: Text('Like'),
),
nopeTag: Container(
margin: const EdgeInsets.all(15.0),
padding: const EdgeInsets.all(3.0),
- decoration: BoxDecoration(
- border: Border.all(color: Colors.red)
- ),
+ decoration: BoxDecoration(border: Border.all(color: Colors.red)),
child: Text('Nope'),
),
superLikeTag: Container(
margin: const EdgeInsets.all(15.0),
padding: const EdgeInsets.all(3.0),
- decoration: BoxDecoration(
- border: Border.all(color: Colors.orange)
- ),
+ decoration: BoxDecoration(border: Border.all(color: Colors.orange)),
child: Text('Super Like'),
),
),
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 07672ca..e826619 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -5,42 +5,48 @@ packages:
dependency: transitive
description:
name: async
- url: "https://pub.dartlang.org"
+ sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
+ url: "https://pub.dev"
source: hosted
- version: "2.9.0"
+ version: "2.11.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
- url: "https://pub.dartlang.org"
+ sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.0"
+ version: "2.1.1"
characters:
dependency: transitive
description:
name: characters
- url: "https://pub.dartlang.org"
+ sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+ url: "https://pub.dev"
source: hosted
- version: "1.2.1"
+ version: "1.3.0"
clock:
dependency: transitive
description:
name: clock
- url: "https://pub.dartlang.org"
+ sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+ url: "https://pub.dev"
source: hosted
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
- url: "https://pub.dartlang.org"
+ sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
+ url: "https://pub.dev"
source: hosted
- version: "1.16.0"
+ version: "1.17.1"
fake_async:
dependency: transitive
description:
name: fake_async
- url: "https://pub.dartlang.org"
+ sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+ url: "https://pub.dev"
source: hosted
version: "1.3.1"
flutter:
@@ -53,34 +59,46 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ js:
+ dependency: transitive
+ description:
+ name: js
+ sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.6.7"
matcher:
dependency: transitive
description:
name: matcher
- url: "https://pub.dartlang.org"
+ sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
+ url: "https://pub.dev"
source: hosted
- version: "0.12.12"
+ version: "0.12.15"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
- url: "https://pub.dartlang.org"
+ sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
+ url: "https://pub.dev"
source: hosted
- version: "0.1.5"
+ version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
- url: "https://pub.dartlang.org"
+ sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
+ url: "https://pub.dev"
source: hosted
- version: "1.8.0"
+ version: "1.9.1"
path:
dependency: transitive
description:
name: path
- url: "https://pub.dartlang.org"
+ sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
+ url: "https://pub.dev"
source: hosted
- version: "1.8.2"
+ version: "1.8.3"
sky_engine:
dependency: transitive
description: flutter
@@ -90,58 +108,65 @@ packages:
dependency: transitive
description:
name: source_span
- url: "https://pub.dartlang.org"
+ sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
+ url: "https://pub.dev"
source: hosted
- version: "1.9.0"
+ version: "1.9.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
- url: "https://pub.dartlang.org"
+ sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
+ url: "https://pub.dev"
source: hosted
- version: "1.10.0"
+ version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
- url: "https://pub.dartlang.org"
+ sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.0"
+ version: "2.1.1"
string_scanner:
dependency: transitive
description:
name: string_scanner
- url: "https://pub.dartlang.org"
+ sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+ url: "https://pub.dev"
source: hosted
- version: "1.1.1"
+ version: "1.2.0"
swipe_cards:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
- version: "2.0.0"
+ version: "2.1.1+2"
term_glyph:
dependency: transitive
description:
name: term_glyph
- url: "https://pub.dartlang.org"
+ sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
+ url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
- url: "https://pub.dartlang.org"
+ sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
+ url: "https://pub.dev"
source: hosted
- version: "0.4.12"
+ version: "0.5.1"
vector_math:
dependency: transitive
description:
name: vector_math
- url: "https://pub.dartlang.org"
+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ version: "2.1.4"
sdks:
- dart: ">=2.17.5 <3.0.0"
+ dart: ">=3.0.0-0 <4.0.0"
flutter: ">=1.17.0"
diff --git a/lib/draggable_card.dart b/lib/draggable_card.dart
index 209cb75..c90706d 100644
--- a/lib/draggable_card.dart
+++ b/lib/draggable_card.dart
@@ -15,6 +15,8 @@ class DraggableCard extends StatefulWidget {
final bool isDraggable;
final SlideDirection? slideTo;
final Function(double distance)? onSlideUpdate;
+ final Function(SlideRegion? slideRegion)? onSwipeChange;
+ final Function(SlideRegion? slideRegion)? onSwipeFinish;
final Function(SlideRegion? slideRegion)? onSlideRegionUpdate;
final Function(SlideDirection? direction)? onSlideOutComplete;
final bool upSwipeAllowed;
@@ -22,29 +24,41 @@ class DraggableCard extends StatefulWidget {
final bool rightSwipeAllowed;
final EdgeInsets padding;
final bool isBackCard;
-
- DraggableCard(
- {this.card,
- this.likeTag,
- this.nopeTag,
- this.superLikeTag,
- this.isDraggable = true,
- this.onSlideUpdate,
- this.onSlideOutComplete,
- this.slideTo,
- this.onSlideRegionUpdate,
- this.upSwipeAllowed = false,
- this.leftSwipeAllowed = true,
- this.rightSwipeAllowed = true,
- this.isBackCard = false,
- this.padding = EdgeInsets.zero});
+ final double leftSwipeThreshold;
+ final double rightSwipeThreshold;
+ final double topSwipeThreshold;
+ final double nopeTagAngle;
+ final double likeTagAngle;
+
+ DraggableCard({
+ this.card,
+ this.likeTag,
+ this.nopeTag,
+ this.superLikeTag,
+ this.isDraggable = true,
+ this.onSlideUpdate,
+ this.onSwipeChange,
+ this.onSwipeFinish,
+ this.onSlideOutComplete,
+ this.slideTo,
+ this.onSlideRegionUpdate,
+ this.upSwipeAllowed = false,
+ this.leftSwipeAllowed = true,
+ this.rightSwipeAllowed = true,
+ this.isBackCard = false,
+ this.padding = EdgeInsets.zero,
+ this.leftSwipeThreshold = -0.45,
+ this.rightSwipeThreshold = 0.45,
+ this.topSwipeThreshold = -0.4,
+ this.nopeTagAngle = -12,
+ this.likeTagAngle = 12,
+ });
@override
_DraggableCardState createState() => _DraggableCardState();
}
-class _DraggableCardState extends State
- with TickerProviderStateMixin {
+class _DraggableCardState extends State with TickerProviderStateMixin {
GlobalKey profileCardKey = GlobalKey(debugLabel: 'profile_card_key');
Offset? cardOffset = const Offset(0.0, 0.0);
Offset? dragStart;
@@ -158,11 +172,8 @@ class _DraggableCardState extends State
Offset _chooseRandomDragStart() {
final cardContext = profileCardKey.currentContext!;
- final cardTopLeft = (cardContext.findRenderObject() as RenderBox)
- .localToGlobal(const Offset(0.0, 0.0));
- final dragStartY =
- cardContext.size!.height * (Random().nextDouble() < 0.5 ? 0.25 : 0.75) +
- cardTopLeft.dy;
+ final cardTopLeft = (cardContext.findRenderObject() as RenderBox).localToGlobal(const Offset(0.0, 0.0));
+ final dragStartY = cardContext.size!.height * (Random().nextDouble() < 0.5 ? 0.25 : 0.75) + cardTopLeft.dy;
return Offset(cardContext.size!.width / 2 + cardTopLeft.dx, dragStartY);
}
@@ -170,8 +181,7 @@ class _DraggableCardState extends State
await Future.delayed(Duration(milliseconds: 1)).then((_) {
final screenWidth = context.size!.width;
dragStart = _chooseRandomDragStart();
- slideOutTween = Tween(
- begin: const Offset(0.0, 0.0), end: Offset(-2 * screenWidth, 0.0));
+ slideOutTween = Tween(begin: const Offset(0.0, 0.0), end: Offset(-2 * screenWidth, 0.0));
slideOutAnimation.forward(from: 0.0);
});
}
@@ -180,8 +190,7 @@ class _DraggableCardState extends State
await Future.delayed(Duration(milliseconds: 1)).then((_) {
final screenWidth = context.size!.width;
dragStart = _chooseRandomDragStart();
- slideOutTween = Tween(
- begin: const Offset(0.0, 0.0), end: Offset(2 * screenWidth, 0.0));
+ slideOutTween = Tween(begin: const Offset(0.0, 0.0), end: Offset(2 * screenWidth, 0.0));
slideOutAnimation.forward(from: 0.0);
});
}
@@ -190,8 +199,7 @@ class _DraggableCardState extends State
await Future.delayed(Duration(milliseconds: 1)).then((_) {
final screenHeight = context.size!.height;
dragStart = _chooseRandomDragStart();
- slideOutTween = Tween(
- begin: const Offset(0.0, 0.0), end: Offset(0.0, -2 * screenHeight));
+ slideOutTween = Tween(begin: const Offset(0.0, 0.0), end: Offset(0.0, -2 * screenHeight));
slideOutAnimation.forward(from: 0.0);
});
}
@@ -205,15 +213,14 @@ class _DraggableCardState extends State
}
void _onPanUpdate(DragUpdateDetails details) {
- final isInLeftRegion = (cardOffset!.dx / context.size!.width) < -0.45;
- final isInRightRegion = (cardOffset!.dx / context.size!.width) > 0.45;
- final isInTopRegion = (cardOffset!.dy / context.size!.height) < -0.40;
+ /// Added customizable swipe thresholds. Old values used by default.
+ final isInLeftRegion = (cardOffset!.dx / context.size!.width) < widget.leftSwipeThreshold;
+ final isInRightRegion = (cardOffset!.dx / context.size!.width) >= widget.rightSwipeThreshold;
+ final isInTopRegion = (cardOffset!.dy / context.size!.height) < widget.topSwipeThreshold;
setState(() {
if (isInLeftRegion || isInRightRegion) {
- slideRegion = isInLeftRegion
- ? SlideRegion.inNopeRegion
- : SlideRegion.inLikeRegion;
+ slideRegion = isInLeftRegion ? SlideRegion.inNopeRegion : SlideRegion.inLikeRegion;
} else if (isInTopRegion) {
slideRegion = SlideRegion.inSuperLikeRegion;
} else {
@@ -223,6 +230,10 @@ class _DraggableCardState extends State
dragPosition = details.globalPosition;
cardOffset = dragPosition! - dragStart!;
+ if (null != widget.onSwipeChange) {
+ widget.onSwipeChange!(slideRegion);
+ }
+
if (null != widget.onSlideUpdate) {
widget.onSlideUpdate!(cardOffset!.distance);
}
@@ -236,15 +247,15 @@ class _DraggableCardState extends State
void _onPanEnd(DragEndDetails details) {
final dragVector = cardOffset! / cardOffset!.distance;
- final isInLeftRegion = (cardOffset!.dx / context.size!.width) < -0.15;
- final isInRightRegion = (cardOffset!.dx / context.size!.width) > 0.15;
- final isInTopRegion = (cardOffset!.dy / context.size!.height) < -0.15;
+ /// Threshold for resulting action is the same as threshold for showing Nope / Like action widgets
+ final isInLeftRegion = (cardOffset!.dx / context.size!.width) < widget.leftSwipeThreshold;
+ final isInRightRegion = (cardOffset!.dx / context.size!.width) >= widget.rightSwipeThreshold;
+ final isInTopRegion = (cardOffset!.dy / context.size!.height) < widget.topSwipeThreshold;
setState(() {
if (isInLeftRegion) {
if (widget.leftSwipeAllowed) {
- slideOutTween = Tween(
- begin: cardOffset, end: dragVector * (2 * context.size!.width));
+ slideOutTween = Tween(begin: cardOffset, end: dragVector * (2 * context.size!.width));
slideOutAnimation.forward(from: 0.0);
slideOutDirection = SlideDirection.left;
@@ -254,8 +265,7 @@ class _DraggableCardState extends State
}
} else if (isInRightRegion) {
if (widget.rightSwipeAllowed) {
- slideOutTween = Tween(
- begin: cardOffset, end: dragVector * (2 * context.size!.width));
+ slideOutTween = Tween(begin: cardOffset, end: dragVector * (2 * context.size!.width));
slideOutAnimation.forward(from: 0.0);
slideOutDirection = SlideDirection.right;
@@ -265,8 +275,7 @@ class _DraggableCardState extends State
}
} else if (isInTopRegion) {
if (widget.upSwipeAllowed) {
- slideOutTween = Tween(
- begin: cardOffset, end: dragVector * (2 * context.size!.height));
+ slideOutTween = Tween(begin: cardOffset, end: dragVector * (2 * context.size!.height));
slideOutAnimation.forward(from: 0.0);
slideOutDirection = SlideDirection.up;
@@ -279,6 +288,19 @@ class _DraggableCardState extends State
slideBackAnimation.forward(from: 0.0);
}
+ /// Added direct notification for swipe finished gesture
+ if (null != widget.onSwipeFinish) {
+ final region;
+ if (isInLeftRegion || isInRightRegion) {
+ region = isInLeftRegion ? SlideRegion.inNopeRegion : SlideRegion.inLikeRegion;
+ } else if (isInTopRegion) {
+ region = SlideRegion.inSuperLikeRegion;
+ } else {
+ region = null;
+ }
+ widget.onSwipeFinish!(region);
+ }
+
slideRegion = null;
if (null != widget.onSlideRegionUpdate) {
widget.onSlideRegionUpdate!(slideRegion);
@@ -288,11 +310,8 @@ class _DraggableCardState extends State
double _rotation(Rect? dragBounds) {
if (dragStart != null) {
- final rotationCornerMultiplier =
- dragStart!.dy >= dragBounds!.top + (dragBounds.height / 2) ? -1 : 1;
- return (pi / 8) *
- (cardOffset!.dx / dragBounds.width) *
- rotationCornerMultiplier;
+ final rotationCornerMultiplier = dragStart!.dy >= dragBounds!.top + (dragBounds.height / 2) ? -1 : 1;
+ return (pi / 8) * (cardOffset!.dx / dragBounds.width) * rotationCornerMultiplier;
} else {
return 0.0;
}
@@ -314,15 +333,12 @@ class _DraggableCardState extends State
//Disables dragging card while slide out animation is in progress. Solves
// issue that fast swipes cause the back card not loading
- if (widget.isBackCard &&
- anchorBounds != null &&
- cardOffset!.dx < anchorBounds!.height) {
+ if (widget.isBackCard && anchorBounds != null && cardOffset!.dx < anchorBounds!.height) {
cardOffset = Offset.zero;
}
return Transform(
- transform: Matrix4.translationValues(cardOffset!.dx, cardOffset!.dy, 0.0)
- ..rotateZ(_rotation(anchorBounds)),
+ transform: Matrix4.translationValues(cardOffset!.dx, cardOffset!.dy, 0.0)..rotateZ(_rotation(anchorBounds)),
origin: _rotationOrigin(anchorBounds),
child: Container(
key: profileCardKey,
@@ -337,28 +353,25 @@ class _DraggableCardState extends State
? Stack(
children: [
widget.card!,
- if (widget.likeTag != null &&
- slideRegion == SlideRegion.inLikeRegion)
+ if (widget.likeTag != null && slideRegion == SlideRegion.inLikeRegion)
Positioned(
- top: 40,
- left: 20,
+ top: 0,
+ left: 0,
child: Transform.rotate(
- angle: 12,
+ angle: widget.likeTagAngle,
child: widget.likeTag,
),
),
- if (widget.nopeTag != null &&
- slideRegion == SlideRegion.inNopeRegion)
+ if (widget.nopeTag != null && slideRegion == SlideRegion.inNopeRegion)
Positioned(
- top: 40,
- right: 20,
+ top: 0,
+ right: 0,
child: Transform.rotate(
- angle: -12,
+ angle: widget.nopeTagAngle,
child: widget.nopeTag,
),
),
- if (widget.superLikeTag != null &&
- slideRegion == SlideRegion.inSuperLikeRegion)
+ if (widget.superLikeTag != null && slideRegion == SlideRegion.inSuperLikeRegion)
Align(
alignment: Alignment.bottomCenter,
child: widget.superLikeTag,
@@ -375,8 +388,7 @@ class _DraggableCardState extends State
await Future.delayed(Duration(milliseconds: 3));
box = context.findRenderObject() as RenderBox?;
topLeft = box!.size.topLeft(box!.localToGlobal(const Offset(0.0, 0.0)));
- bottomRight =
- box!.size.bottomRight(box!.localToGlobal(const Offset(0.0, 0.0)));
+ bottomRight = box!.size.bottomRight(box!.localToGlobal(const Offset(0.0, 0.0)));
anchorBounds = new Rect.fromLTRB(
topLeft.dx,
topLeft.dy,
diff --git a/lib/swipe_cards.dart b/lib/swipe_cards.dart
index 7354f9f..5367f90 100644
--- a/lib/swipe_cards.dart
+++ b/lib/swipe_cards.dart
@@ -1,7 +1,9 @@
+// ignore_for_file: public_member_api_docs, sort_constructors_first
library swipe_cards;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
+
import 'package:swipe_cards/draggable_card.dart';
import 'package:swipe_cards/profile_card.dart';
@@ -17,20 +19,44 @@ class SwipeCards extends StatefulWidget {
final bool upSwipeAllowed;
final bool leftSwipeAllowed;
final bool rightSwipeAllowed;
+ final double nopeTagAngle;
+ final double likeTagAngle;
+
+ /// Threshold for left swipe. The larger the number, the further the card needs to be dragged to activate the action. Must be less or equal to 0 to work correct.
+ final double leftSwipeThreshold;
+
+ /// Threshold for right swipe. The larger the number, the further the card needs to be dragged to activate the action. Must be bigger or equal to 0 to work correct.
+ final double rightSwipeThreshold;
+
+ /// Threshold for top swipe. The larger the number, the further the card needs to be dragged to activate the action.
+ final double topSwipeThreshold;
+
+ /// Triggered when user dragging the card
+ final Function(SwipeItem item, SlideRegion? slideRegion)? onSwipeChange;
+
+ /// Triggered when user releases the card
+ final Function(SwipeItem item, SlideRegion? slideRegion)? onSwipeFinish;
SwipeCards({
Key? key,
- required this.matchEngine,
- required this.onStackFinished,
required this.itemBuilder,
this.likeTag,
this.nopeTag,
this.superLikeTag,
+ required this.matchEngine,
+ required this.onStackFinished,
+ this.itemChanged,
this.fillSpace = true,
this.upSwipeAllowed = false,
this.leftSwipeAllowed = true,
this.rightSwipeAllowed = true,
- this.itemChanged,
+ this.leftSwipeThreshold = -0.45,
+ this.rightSwipeThreshold = 0.45,
+ this.topSwipeThreshold = -0.4,
+ this.onSwipeChange,
+ this.onSwipeFinish,
+ this.nopeTagAngle = -12,
+ this.likeTagAngle = 12,
}) : super(key: key);
@override
@@ -124,6 +150,22 @@ class _SwipeCardsState extends State {
});
}
+ void _onSlideFinish(double distance) {
+ setState(() {
+ _nextCardScale = 0.9 + (0.1 * (distance / 100.0)).clamp(0.0, 0.1);
+ });
+ }
+
+ void _onSwipeChange(SlideRegion? region) {
+ final item = widget.matchEngine.currentItem;
+ if (item != null) widget.onSwipeChange?.call(item, region);
+ }
+
+ void _onSwipeFinish(SlideRegion? region) {
+ final item = widget.matchEngine.currentItem;
+ if (item != null) widget.onSwipeFinish?.call(item, region);
+ }
+
void _onSlideRegion(SlideRegion? region) {
setState(() {
slideRegion = region;
@@ -150,15 +192,16 @@ class _SwipeCardsState extends State {
break;
}
- if (widget.matchEngine._nextItemIndex! <
- widget.matchEngine._swipeItems!.length) {
- widget.itemChanged?.call(
- widget.matchEngine.nextItem!, widget.matchEngine._nextItemIndex!);
- }
+ if (direction != null) {
+ if (widget.matchEngine._nextItemIndex! < widget.matchEngine._swipeItems!.length) {
+ widget.itemChanged?.call(widget.matchEngine.nextItem!, widget.matchEngine._nextItemIndex!);
+ }
+ widget.matchEngine.itemChanged?.call(widget.matchEngine.currentItem, widget.matchEngine._currentItemIndex);
- widget.matchEngine.cycleMatch();
- if (widget.matchEngine.currentItem == null) {
- widget.onStackFinished();
+ widget.matchEngine.cycleMatch();
+ if (widget.matchEngine.currentItem == null) {
+ widget.onStackFinished();
+ }
}
}
@@ -197,12 +240,19 @@ class _SwipeCardsState extends State {
superLikeTag: widget.superLikeTag,
slideTo: _desiredSlideOutDirection(),
onSlideUpdate: _onSlideUpdate,
+ onSwipeChange: _onSwipeChange,
+ onSwipeFinish: _onSwipeFinish,
onSlideRegionUpdate: _onSlideRegion,
onSlideOutComplete: _onSlideOutComplete,
upSwipeAllowed: widget.upSwipeAllowed,
leftSwipeAllowed: widget.leftSwipeAllowed,
rightSwipeAllowed: widget.rightSwipeAllowed,
isBackCard: false,
+ leftSwipeThreshold: widget.leftSwipeThreshold,
+ rightSwipeThreshold: widget.rightSwipeThreshold,
+ topSwipeThreshold: widget.topSwipeThreshold,
+ nopeTagAngle: widget.nopeTagAngle,
+ likeTagAngle: widget.likeTagAngle,
)
],
);
@@ -211,31 +261,31 @@ class _SwipeCardsState extends State {
class MatchEngine extends ChangeNotifier {
final List? _swipeItems;
+ List? get swipeItems => _swipeItems;
+
int? _currentItemIndex;
int? _nextItemIndex;
+ Function(SwipeItem?, int?)? itemChanged;
MatchEngine({
List? swipeItems,
+ this.itemChanged,
}) : _swipeItems = swipeItems {
_currentItemIndex = 0;
_nextItemIndex = 1;
}
- SwipeItem? get currentItem => _currentItemIndex! < _swipeItems!.length
- ? _swipeItems![_currentItemIndex!]
- : null;
+ SwipeItem? get currentItem => _currentItemIndex! < _swipeItems!.length ? _swipeItems![_currentItemIndex!] : null;
- SwipeItem? get nextItem => _nextItemIndex! < _swipeItems!.length
- ? _swipeItems![_nextItemIndex!]
- : null;
+ SwipeItem? get nextItem => _nextItemIndex! < _swipeItems!.length ? _swipeItems![_nextItemIndex!] : null;
void cycleMatch() {
- if (currentItem!.decision != Decision.undecided) {
- currentItem!.resetMatch();
- _currentItemIndex = _nextItemIndex;
- _nextItemIndex = _nextItemIndex! + 1;
- notifyListeners();
- }
+ //if (currentItem!.decision != Decision.undecided) {
+ currentItem!.resetMatch();
+ _currentItemIndex = _nextItemIndex;
+ _nextItemIndex = _nextItemIndex! + 1;
+ notifyListeners();
+ //}
}
void rewindMatch() {
@@ -255,6 +305,7 @@ class SwipeItem extends ChangeNotifier {
final Function? superlikeAction;
final Function? nopeAction;
final Future Function(SlideRegion? slideRegion)? onSlideUpdate;
+ final Future Function(SlideRegion? slideRegion)? onSlideFinish;
Decision decision = Decision.undecided;
SwipeItem({
@@ -263,6 +314,7 @@ class SwipeItem extends ChangeNotifier {
this.superlikeAction,
this.nopeAction,
this.onSlideUpdate,
+ this.onSlideFinish,
});
void slideUpdateAction(SlideRegion? slideRegion) async {
diff --git a/pubspec.lock b/pubspec.lock
index bffca08..e19afa9 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -5,42 +5,48 @@ packages:
dependency: transitive
description:
name: async
- url: "https://pub.dartlang.org"
+ sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
+ url: "https://pub.dev"
source: hosted
- version: "2.9.0"
+ version: "2.11.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
- url: "https://pub.dartlang.org"
+ sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.0"
+ version: "2.1.1"
characters:
dependency: transitive
description:
name: characters
- url: "https://pub.dartlang.org"
+ sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+ url: "https://pub.dev"
source: hosted
- version: "1.2.1"
+ version: "1.3.0"
clock:
dependency: transitive
description:
name: clock
- url: "https://pub.dartlang.org"
+ sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+ url: "https://pub.dev"
source: hosted
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
- url: "https://pub.dartlang.org"
+ sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
+ url: "https://pub.dev"
source: hosted
- version: "1.16.0"
+ version: "1.17.1"
fake_async:
dependency: transitive
description:
name: fake_async
- url: "https://pub.dartlang.org"
+ sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+ url: "https://pub.dev"
source: hosted
version: "1.3.1"
flutter:
@@ -53,34 +59,46 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ js:
+ dependency: transitive
+ description:
+ name: js
+ sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.6.7"
matcher:
dependency: transitive
description:
name: matcher
- url: "https://pub.dartlang.org"
+ sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
+ url: "https://pub.dev"
source: hosted
- version: "0.12.12"
+ version: "0.12.15"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
- url: "https://pub.dartlang.org"
+ sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
+ url: "https://pub.dev"
source: hosted
- version: "0.1.5"
+ version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
- url: "https://pub.dartlang.org"
+ sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
+ url: "https://pub.dev"
source: hosted
- version: "1.8.0"
+ version: "1.9.1"
path:
dependency: transitive
description:
name: path
- url: "https://pub.dartlang.org"
+ sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
+ url: "https://pub.dev"
source: hosted
- version: "1.8.2"
+ version: "1.8.3"
sky_engine:
dependency: transitive
description: flutter
@@ -90,51 +108,58 @@ packages:
dependency: transitive
description:
name: source_span
- url: "https://pub.dartlang.org"
+ sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
+ url: "https://pub.dev"
source: hosted
- version: "1.9.0"
+ version: "1.9.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
- url: "https://pub.dartlang.org"
+ sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
+ url: "https://pub.dev"
source: hosted
- version: "1.10.0"
+ version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
- url: "https://pub.dartlang.org"
+ sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.0"
+ version: "2.1.1"
string_scanner:
dependency: transitive
description:
name: string_scanner
- url: "https://pub.dartlang.org"
+ sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+ url: "https://pub.dev"
source: hosted
- version: "1.1.1"
+ version: "1.2.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
- url: "https://pub.dartlang.org"
+ sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
+ url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
- url: "https://pub.dartlang.org"
+ sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
+ url: "https://pub.dev"
source: hosted
- version: "0.4.12"
+ version: "0.5.1"
vector_math:
dependency: transitive
description:
name: vector_math
- url: "https://pub.dartlang.org"
+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ version: "2.1.4"
sdks:
- dart: ">=2.17.5 <3.0.0"
+ dart: ">=3.0.0-0 <4.0.0"
flutter: ">=1.17.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 773fe11..43b2b22 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: swipe_cards
description: Tinder like swipe cards.
-version: 2.0.0+1
+version: 2.1.1+2
homepage: https://github.com/AnupKumarPanwar/swipe_cards
environment: