From 5bbcb578dce359716a6ca85ba888ab89723b3ed4 Mon Sep 17 00:00:00 2001 From: Dmitry Mikheev Date: Sat, 19 Aug 2023 13:36:54 +0200 Subject: [PATCH 1/8] Swipe threshold. New events: onSwipeChange and onSwipeFinish 1. Added properties: - leftSwipeThreshold - rightSwipeThreshold - topSwipeThreshold They regulate distance that triggers nope and like widgets to appear. 2. Thresholds for triggering nope/like events are set to be equal than thresholds for showing nope/like widgets. Now, if you don't see nope/like widgets and release the card - the card will return to original place. 3. Added direct events in SwipeCards widget: Function(SwipeItem item, SlideRegion? slideRegion)? onSwipeChange; Function(SwipeItem item, SlideRegion? slideRegion)? onSwipeFinish; They will be triggered when user drags the card of release the card. --- example/ios/Runner.xcodeproj/project.pbxproj | 5 +- example/ios/Runner/Info.plist | 2 + example/lib/main.dart | 31 ++--- example/pubspec.lock | 91 ++++++++----- lib/draggable_card.dart | 132 ++++++++++--------- lib/swipe_cards.dart | 55 ++++++-- pubspec.lock | 89 ++++++++----- 7 files changed, 245 insertions(+), 160 deletions(-) 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..cdc3825 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.0.0+1" 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..ac8df71 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,37 @@ 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; + + 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, + }); @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 +168,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 +177,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 +186,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 +195,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 +209,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 +226,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 +243,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 +261,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 +271,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 +284,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 +306,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 +329,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,8 +349,7 @@ 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, @@ -347,8 +358,7 @@ class _DraggableCardState extends State child: widget.likeTag, ), ), - if (widget.nopeTag != null && - slideRegion == SlideRegion.inNopeRegion) + if (widget.nopeTag != null && slideRegion == SlideRegion.inNopeRegion) Positioned( top: 40, right: 20, @@ -357,8 +367,7 @@ class _DraggableCardState extends State 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 +384,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..30e0550 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,30 @@ class SwipeCards extends StatefulWidget { final bool upSwipeAllowed; final bool leftSwipeAllowed; final bool rightSwipeAllowed; + final double leftSwipeThreshold; + final double rightSwipeThreshold; + final double topSwipeThreshold; + final Function(SwipeItem item, SlideRegion? slideRegion)? onSwipeChange; + 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, }) : super(key: key); @override @@ -124,6 +136,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,10 +178,8 @@ class _SwipeCardsState extends State { break; } - if (widget.matchEngine._nextItemIndex! < - widget.matchEngine._swipeItems!.length) { - widget.itemChanged?.call( - widget.matchEngine.nextItem!, widget.matchEngine._nextItemIndex!); + if (widget.matchEngine._nextItemIndex! < widget.matchEngine._swipeItems!.length) { + widget.itemChanged?.call(widget.matchEngine.nextItem!, widget.matchEngine._nextItemIndex!); } widget.matchEngine.cycleMatch(); @@ -197,12 +223,17 @@ 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, ) ], ); @@ -221,13 +252,9 @@ class MatchEngine extends ChangeNotifier { _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) { @@ -255,6 +282,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 +291,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" From 7b16dd5adc75bbf2d7310299e303db39f07e99ef Mon Sep 17 00:00:00 2001 From: Dmitry Mikheev Date: Sat, 19 Aug 2023 13:44:05 +0200 Subject: [PATCH 2/8] Added comments --- lib/swipe_cards.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/swipe_cards.dart b/lib/swipe_cards.dart index 30e0550..a691cc6 100644 --- a/lib/swipe_cards.dart +++ b/lib/swipe_cards.dart @@ -19,10 +19,20 @@ class SwipeCards extends StatefulWidget { final bool upSwipeAllowed; final bool leftSwipeAllowed; final bool rightSwipeAllowed; + + /// 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({ From 45524309cd9864d8ec6cc8a098392278d20e6b5e Mon Sep 17 00:00:00 2001 From: Dmytro Mikheiev Date: Sat, 19 Aug 2023 14:17:01 +0200 Subject: [PATCH 3/8] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) 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 From 5c2320cc9dbb0c3cfafe39145331ead01c448e24 Mon Sep 17 00:00:00 2001 From: Dmitry Mikheev Date: Sat, 19 Aug 2023 15:05:31 +0200 Subject: [PATCH 4/8] nopeTagAngle and likeTagAngle 1. Added nopeTagAngle and likeTagAngle 2. Removed default padding for nopeTag and likeTag --- lib/draggable_card.dart | 16 ++++++++++------ lib/swipe_cards.dart | 6 ++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/draggable_card.dart b/lib/draggable_card.dart index ac8df71..c90706d 100644 --- a/lib/draggable_card.dart +++ b/lib/draggable_card.dart @@ -27,6 +27,8 @@ class DraggableCard extends StatefulWidget { final double leftSwipeThreshold; final double rightSwipeThreshold; final double topSwipeThreshold; + final double nopeTagAngle; + final double likeTagAngle; DraggableCard({ this.card, @@ -48,6 +50,8 @@ class DraggableCard extends StatefulWidget { this.leftSwipeThreshold = -0.45, this.rightSwipeThreshold = 0.45, this.topSwipeThreshold = -0.4, + this.nopeTagAngle = -12, + this.likeTagAngle = 12, }); @override @@ -351,19 +355,19 @@ class _DraggableCardState extends State with TickerProviderStateM widget.card!, 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) Positioned( - top: 40, - right: 20, + top: 0, + right: 0, child: Transform.rotate( - angle: -12, + angle: widget.nopeTagAngle, child: widget.nopeTag, ), ), diff --git a/lib/swipe_cards.dart b/lib/swipe_cards.dart index a691cc6..73ded25 100644 --- a/lib/swipe_cards.dart +++ b/lib/swipe_cards.dart @@ -19,6 +19,8 @@ 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; @@ -53,6 +55,8 @@ class SwipeCards extends StatefulWidget { this.topSwipeThreshold = -0.4, this.onSwipeChange, this.onSwipeFinish, + this.nopeTagAngle = -12, + this.likeTagAngle = 12, }) : super(key: key); @override @@ -244,6 +248,8 @@ class _SwipeCardsState extends State { leftSwipeThreshold: widget.leftSwipeThreshold, rightSwipeThreshold: widget.rightSwipeThreshold, topSwipeThreshold: widget.topSwipeThreshold, + nopeTagAngle: widget.nopeTagAngle, + likeTagAngle: widget.likeTagAngle, ) ], ); From d961e2e9ca4f393970dbc761d04c5da44a6fecf1 Mon Sep 17 00:00:00 2001 From: Dmitry Mikheev Date: Sat, 19 Aug 2023 15:13:14 +0200 Subject: [PATCH 5/8] Updated app version --- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index cdc3825..08b7f8c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -142,7 +142,7 @@ packages: path: ".." relative: true source: path - version: "2.0.0+1" + version: "2.1.0+1" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 773fe11..d57c61c 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.0+1 homepage: https://github.com/AnupKumarPanwar/swipe_cards environment: From 1697dfafb6aad2af887bd9b22091941fe6f60cc8 Mon Sep 17 00:00:00 2001 From: Dmitry Mikheev Date: Tue, 5 Sep 2023 11:59:23 +0200 Subject: [PATCH 6/8] Added public access to swipeItems inside MatchEngine --- example/pubspec.lock | 2 +- lib/swipe_cards.dart | 2 ++ pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 08b7f8c..3a20125 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -142,7 +142,7 @@ packages: path: ".." relative: true source: path - version: "2.1.0+1" + version: "2.1.1+1" term_glyph: dependency: transitive description: diff --git a/lib/swipe_cards.dart b/lib/swipe_cards.dart index 73ded25..6daf490 100644 --- a/lib/swipe_cards.dart +++ b/lib/swipe_cards.dart @@ -258,6 +258,8 @@ class _SwipeCardsState extends State { class MatchEngine extends ChangeNotifier { final List? _swipeItems; + List? get swipeItems => _swipeItems; + int? _currentItemIndex; int? _nextItemIndex; diff --git a/pubspec.yaml b/pubspec.yaml index d57c61c..28b6247 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: swipe_cards description: Tinder like swipe cards. -version: 2.1.0+1 +version: 2.1.1+1 homepage: https://github.com/AnupKumarPanwar/swipe_cards environment: From cc94da74f2868e6dc8f2cd31faef65862373555b Mon Sep 17 00:00:00 2001 From: Dmitry Mikheev Date: Tue, 5 Sep 2023 13:56:29 +0200 Subject: [PATCH 7/8] Modification for own project --- lib/swipe_cards.dart | 29 +++++++++++++++++------------ pubspec.yaml | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/swipe_cards.dart b/lib/swipe_cards.dart index 6daf490..5367f90 100644 --- a/lib/swipe_cards.dart +++ b/lib/swipe_cards.dart @@ -192,13 +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(); + } } } @@ -262,9 +265,11 @@ class MatchEngine extends ChangeNotifier { int? _currentItemIndex; int? _nextItemIndex; + Function(SwipeItem?, int?)? itemChanged; MatchEngine({ List? swipeItems, + this.itemChanged, }) : _swipeItems = swipeItems { _currentItemIndex = 0; _nextItemIndex = 1; @@ -275,12 +280,12 @@ class MatchEngine extends ChangeNotifier { 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() { diff --git a/pubspec.yaml b/pubspec.yaml index 28b6247..43b2b22 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: swipe_cards description: Tinder like swipe cards. -version: 2.1.1+1 +version: 2.1.1+2 homepage: https://github.com/AnupKumarPanwar/swipe_cards environment: From ba69402eb4855af0336aac980db0543e0072a13c Mon Sep 17 00:00:00 2001 From: Dmitry Mikheev Date: Tue, 5 Sep 2023 13:56:37 +0200 Subject: [PATCH 8/8] Update pubspec.lock --- example/pubspec.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 3a20125..e826619 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -142,7 +142,7 @@ packages: path: ".." relative: true source: path - version: "2.1.1+1" + version: "2.1.1+2" term_glyph: dependency: transitive description: