From 976df0eaa24548e82b48041aa11ce163de7a26ca Mon Sep 17 00:00:00 2001 From: Nicholas Albion Date: Fri, 26 Feb 2021 18:04:53 +1100 Subject: [PATCH 1/3] allow full-screen cards (with odd swiping behavior) --- lib/flutter_tindercard.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/flutter_tindercard.dart b/lib/flutter_tindercard.dart index 0d9787c..410bda2 100644 --- a/lib/flutter_tindercard.dart +++ b/lib/flutter_tindercard.dart @@ -68,7 +68,7 @@ class TinderSwapCard extends StatefulWidget { }) : assert(stackNum > 1), assert(swipeEdge > 0), assert(swipeEdgeVertical > 0), - assert(maxWidth > minWidth && maxHeight > minHeight), + assert(maxWidth >= minWidth && maxHeight >= minHeight), _cardBuilder = cardBuilder, _totalNum = totalNum, _stackNum = stackNum, @@ -81,14 +81,14 @@ class TinderSwapCard extends StatefulWidget { final widthGap = maxWidth - minWidth; final heightGap = maxHeight - minHeight; - for (var i = 0; i < _stackNum; i++) { + int i = _stackNum; + while (i-- != 0) { _cardSizes.add( - Size(minWidth + (widthGap / _stackNum) * i, - minHeight + (heightGap / _stackNum) * i), + Size(maxWidth - (widthGap / _stackNum) * i, maxHeight - (heightGap / _stackNum) * i), ); switch (orientation) { - case AmassOrientation.bottom: + case AmassOrientation.top: _cardAligns.add( Alignment( 0.0, @@ -96,7 +96,7 @@ class TinderSwapCard extends StatefulWidget { ), ); break; - case AmassOrientation.top: + case AmassOrientation.bottom: _cardAligns.add( Alignment( 0.0, From f415a60fab5ddd7301d2df5b42e6f90aefcd548e Mon Sep 17 00:00:00 2001 From: Nicholas Albion Date: Sat, 27 Feb 2021 12:31:14 +1100 Subject: [PATCH 2/3] WIP - use scale and position instead of size and alignment. Alignment does not work when card is full-screen. --- lib/flutter_tindercard.dart | 243 ++++++++++++++++++++++++++---------- 1 file changed, 180 insertions(+), 63 deletions(-) diff --git a/lib/flutter_tindercard.dart b/lib/flutter_tindercard.dart index 410bda2..dbf059f 100644 --- a/lib/flutter_tindercard.dart +++ b/lib/flutter_tindercard.dart @@ -32,7 +32,9 @@ class TinderSwapCard extends StatefulWidget { final CardController cardController; - final List _cardSizes = []; + // final List _cardSizes = []; + final Size _cardSize; + final List _cardScales = []; final List _cardAligns = []; @@ -59,8 +61,7 @@ class TinderSwapCard extends StatefulWidget { bool swipeDown = false, double maxWidth, double maxHeight, - double minWidth, - double minHeight, + double scaleFactor = 0.1, bool allowVerticalMovement = true, this.cardController, this.swipeCompleteCallback, @@ -68,7 +69,6 @@ class TinderSwapCard extends StatefulWidget { }) : assert(stackNum > 1), assert(swipeEdge > 0), assert(swipeEdgeVertical > 0), - assert(maxWidth >= minWidth && maxHeight >= minHeight), _cardBuilder = cardBuilder, _totalNum = totalNum, _stackNum = stackNum, @@ -77,15 +77,11 @@ class TinderSwapCard extends StatefulWidget { _swipeEdgeVertical = swipeEdgeVertical, _swipeUp = swipeUp, _swipeDown = swipeDown, - _allowVerticalMovement = allowVerticalMovement { - final widthGap = maxWidth - minWidth; - final heightGap = maxHeight - minHeight; - + _allowVerticalMovement = allowVerticalMovement, + _cardSize = Size(maxWidth, maxHeight) { int i = _stackNum; while (i-- != 0) { - _cardSizes.add( - Size(maxWidth - (widthGap / _stackNum) * i, maxHeight - (heightGap / _stackNum) * i), - ); + _cardScales.add(1 - scaleFactor * i); switch (orientation) { case AmassOrientation.top: @@ -128,6 +124,12 @@ class TinderSwapCard extends StatefulWidget { class _TinderSwapCardState extends State with TickerProviderStateMixin { Alignment frontCardAlign; + // Matrix4 transformation; // = Matrix4.zero(); + // Matrix4 transformation = Matrix4.translationValues(0.0, 0.0, 0.0); + // double x; + // double y; + Rect _positionRect; + Offset _offset = Offset.zero; AnimationController _animationController; @@ -141,8 +143,45 @@ class _TinderSwapCardState extends State } final index = realIndex - _currentFront; + final Widget card = SizedBox.fromSize( + size: widget._cardSize, + child: widget._cardBuilder(context, widget._totalNum - realIndex - 1)); + if (index == widget._stackNum - 1) { - return Align( + final angle = (pi / 180.0) * + (_animationController.status == AnimationStatus.forward + ? CardAnimation.frontCardRota( + _animationController, frontCardAlign.x) + .value + : frontCardAlign.x); + + // final angle = 0.0; + final transformation = Matrix4.identity(); //rotationZ(angle); + transformation.rotateZ(angle * 2); + // transformation.scale(widget._cardScales[index]); + // transformation.translate(20.0, 20.0); + // transformation.scale(1.5); + + // return Transform(transform: transformation, child: card); + + Offset offset = _animationController.status == AnimationStatus.forward + ? CardAnimation.frontCardOffset( + _animationController, + _offset, + Offset.zero, + widget._swipeEdge, + widget._swipeUp, + widget._swipeDown) + .value + : _offset; + + Rect _positionRect = Rect.fromLTWH(offset.dx * 3, offset.dy * 3, + widget._cardSize.width, widget._cardSize.height); + return Positioned.fromRect( + rect: _positionRect, + child: Transform(transform: transformation, child: card)); + + /*return Align( alignment: _animationController.status == AnimationStatus.forward ? frontCardAlign = CardAnimation.frontCardAlign( _animationController, @@ -153,58 +192,42 @@ class _TinderSwapCardState extends State widget._swipeDown, ).value : frontCardAlign, - child: Transform.rotate( - angle: (pi / 180.0) * - (_animationController.status == AnimationStatus.forward - ? CardAnimation.frontCardRota( - _animationController, frontCardAlign.x) - .value - : frontCardAlign.x), - child: SizedBox.fromSize( - size: widget._cardSizes[index], - child: widget._cardBuilder( - context, - widget._totalNum - realIndex - 1, - ), - ), - ), - ); + child: Transform( + transform: transformation, + child: card, + ));*/ } return Align( - alignment: _animationController.status == AnimationStatus.forward && - (frontCardAlign.x > 3.0 || - frontCardAlign.x < -3.0 || - frontCardAlign.y > 3 || - frontCardAlign.y < -3) + alignment: _animationController.status == AnimationStatus.forward + // && (frontCardAlign.x > 3.0 || frontCardAlign.x < -3.0 || frontCardAlign.y > 3 || frontCardAlign.y < -3) ? CardAnimation.backCardAlign( _animationController, widget._cardAligns[index], widget._cardAligns[index + 1], ).value : widget._cardAligns[index], - child: SizedBox.fromSize( - size: _animationController.status == AnimationStatus.forward && - (frontCardAlign.x > 3.0 || - frontCardAlign.x < -3.0 || - frontCardAlign.y > 3 || - frontCardAlign.y < -3) - ? CardAnimation.backCardSize( + child: Transform.scale( + scale: _animationController.status == AnimationStatus.forward + // && (frontCardAlign.x > 3.0 || frontCardAlign.x < -3.0 || frontCardAlign.y > 3 || frontCardAlign.y < -3) + ? CardAnimation.backCardScale( _animationController, - widget._cardSizes[index], - widget._cardSizes[index + 1], + widget._cardScales[index], + widget._cardScales[index + 1], ).value - : widget._cardSizes[index], - child: widget._cardBuilder( - context, - widget._totalNum - realIndex - 1, - ), + : widget._cardScales[index], + child: card, ), ); } List _buildCards(BuildContext context) { final cards = []; + final size = MediaQuery.of(context).size; + + // transformation = Matrix4.translationValues(0.0, 0.0, 0.0); + // transformation = Matrix4.translationValues(50.0, 50.0, 0.0); + // _positionRect = Rect.fromLTRB(0, 0, size.width, size.height); for (var i = _currentFront; i < _currentFront + widget._stackNum; i++) { cards.add(_buildCard(context, i)); @@ -212,25 +235,38 @@ class _TinderSwapCardState extends State cards.add(SizedBox.expand( child: GestureDetector( + onPanStart: (DragStartDetails details) {}, onPanUpdate: (final details) { + // print('onPanUpdate: ${frontCardAlign.x} + ${details.delta.dx} * 20 / ${MediaQuery.of(context).size.width} ' + // '= ${frontCardAlign.x + details.delta.dx * 20 / MediaQuery.of(context).size.width}'); + // print('onPanUpdate: ${details.globalPosition}'); + setState(() { if (widget._allowVerticalMovement == true) { - frontCardAlign = Alignment( - frontCardAlign.x + - details.delta.dx * 20 / MediaQuery.of(context).size.width, - frontCardAlign.y + - details.delta.dy * 30 / MediaQuery.of(context).size.height, + frontCardAlign += Alignment( + 10 * details.delta.dx / (size.width / 2), + 15 * details.delta.dy / (size.height / 2), ); + + // frontCardAlign = Alignment( + // frontCardAlign.x + details.delta.dx * 20 / MediaQuery.of(context).size.width, + // frontCardAlign.y + details.delta.dy * 30 / MediaQuery.of(context).size.height, + // ); + + // transformation.translate(-details.delta.dx * 10, details.delta.dy * 15); + // transformation = Matrix4.translationValues(details.delta.dx * 10, details.delta.dy * 15, 0.0); + // transformation.s + // _positionRect = Rect.fromPoints( + // details.globalPosition, details.globalPosition); + _positionRect = _positionRect.shift(details.delta * 100.0); + _offset += details.delta; + // print('Positioned: $_positionRect'); } else { frontCardAlign = Alignment( frontCardAlign.x + details.delta.dx * 20 / MediaQuery.of(context).size.width, 0, ); - - if (widget.swipeUpdateCallback != null) { - widget.swipeUpdateCallback(details, frontCardAlign); - } } if (widget.swipeUpdateCallback != null) { @@ -283,6 +319,12 @@ class _TinderSwapCardState extends State } void _initState() { + // print('------------------- initialise transformation 0,0,0'); + // transformation = Matrix4.translationValues(0.0, 0.0, 0.0); + _offset = Offset.zero; + _positionRect = Rect.fromLTRB( + 0.0, 0.0, widget._cardSize.width, widget._cardSize.height); + _currentFront = widget._totalNum - widget._stackNum; frontCardAlign = widget._cardAligns[widget._cardAligns.length - 1]; @@ -330,7 +372,10 @@ class _TinderSwapCardState extends State Widget build(BuildContext context) { widget.cardController?.addListener(triggerSwap); - return Stack(children: _buildCards(context)); + return Stack( + // overflow: , + clipBehavior: Clip.none, + children: _buildCards(context)); } void changeCardOrder() { @@ -357,7 +402,7 @@ typedef CardDragUpdateCallback = void Function( enum AmassOrientation { top, bottom, left, right } class CardAnimation { - static Animation frontCardAlign( + /*static Animation frontCardAlign( AnimationController controller, Alignment beginAlign, Alignment baseAlign, @@ -368,6 +413,7 @@ class CardAnimation { double endX, endY; if (_TinderSwapCardState._trigger == TriggerDirection.none) { + // onPanEnd endX = beginAlign.x > 0 ? (beginAlign.x > swipeEdge ? beginAlign.x + 10.0 : baseAlign.x) : (beginAlign.x < -swipeEdge ? beginAlign.x - 10.0 : baseAlign.x); @@ -391,7 +437,7 @@ class CardAnimation { endX = beginAlign.x - swipeEdge; endY = beginAlign.y + 0.5; } - /* Trigger Swipe Up or Down */ + */ /* Trigger Swipe Up or Down */ /* else if (_TinderSwapCardState._trigger == TriggerDirection.up || _TinderSwapCardState._trigger == TriggerDirection.down) { var beginY = @@ -415,6 +461,77 @@ class CardAnimation { curve: Curves.easeOut, ), ); + }*/ + + static Animation frontCardOffset( + AnimationController controller, + Offset beginOffset, + Offset baseOffset, + double swipeEdge, + bool swipeUp, + bool swipeDown, + ) { + double endX, endY; + + if (_TinderSwapCardState._trigger == TriggerDirection.none) { + // onPanEnd + endX = beginOffset.dx > 0 + ? (beginOffset.dx > swipeEdge ? beginOffset.dx + 10.0 : baseOffset.dx) + : (beginOffset.dx < -swipeEdge + ? beginOffset.dx - 10.0 + : baseOffset.dx); + endY = beginOffset.dx > 3.0 || beginOffset.dx < -swipeEdge + ? beginOffset.dy + : baseOffset.dy; + + if (swipeUp || swipeDown) { + if (beginOffset.dy < 0) { + if (swipeUp) { + endY = beginOffset.dy < -swipeEdge + ? beginOffset.dy - 10.0 + : baseOffset.dy; + } + } else if (beginOffset.dy > 0) { + if (swipeDown) { + endY = beginOffset.dy > swipeEdge + ? beginOffset.dy + 10.0 + : baseOffset.dy; + } + } + } + } else if (_TinderSwapCardState._trigger == TriggerDirection.left) { + endX = beginOffset.dx - swipeEdge; + endY = beginOffset.dy + 0.5; + } + /* Trigger Swipe Up or Down */ + else if (_TinderSwapCardState._trigger == TriggerDirection.up || + _TinderSwapCardState._trigger == TriggerDirection.down) { + var beginY = + _TinderSwapCardState._trigger == TriggerDirection.up ? -10 : 10; + + endY = beginY < -swipeEdge ? beginY - 10.0 : baseOffset.dy; + + endX = beginOffset.dx > 0 + ? (beginOffset.dx > swipeEdge ? beginOffset.dx + 10.0 : baseOffset.dx) + : (beginOffset.dx < -swipeEdge + ? beginOffset.dx - 10.0 + : baseOffset.dx); + } else { + endX = beginOffset.dx + swipeEdge; + endY = beginOffset.dy + 0.5; + } + + // print('endX: $endX, begin.x: ${beginOffset.dx}, swipeEdge: $swipeEdge'); + + return Tween( + begin: beginOffset, + end: Offset(endX, endY), + ).animate( + CurvedAnimation( + parent: controller, + curve: Curves.easeOut, + ), + ); } static Animation frontCardRota( @@ -427,12 +544,12 @@ class CardAnimation { ); } - static Animation backCardSize( + static Animation backCardScale( AnimationController controller, - Size beginSize, - Size endSize, + double beginScale, + double endScale, ) { - return SizeTween(begin: beginSize, end: endSize).animate( + return Tween(begin: beginScale, end: endScale).animate( CurvedAnimation( parent: controller, curve: Curves.easeOut, From 01c657abb43dea3b5eae100748cc873a7d022955 Mon Sep 17 00:00:00 2001 From: Nicholas Albion Date: Mon, 1 Mar 2021 16:39:45 +1100 Subject: [PATCH 3/3] restored support for vertical swiping --- example/async_data/lib/main.dart | 10 +- example/example/lib/main.dart | 9 +- lib/flutter_tindercard.dart | 318 +++++++++---------------------- 3 files changed, 98 insertions(+), 239 deletions(-) diff --git a/example/async_data/lib/main.dart b/example/async_data/lib/main.dart index 6071cdc..7648233 100644 --- a/example/async_data/lib/main.dart +++ b/example/async_data/lib/main.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; - import 'package:flutter_tindercard/flutter_tindercard.dart'; void main() => runApp(MyApp()); @@ -110,20 +109,17 @@ class _AsyncDataExampleHomePageState extends State orientation: AmassOrientation.bottom, totalNum: imageList.length, stackNum: 4, - swipeEdge: 4.0, maxWidth: MediaQuery.of(context).size.width * 0.9, maxHeight: MediaQuery.of(context).size.width * 0.9, - minWidth: MediaQuery.of(context).size.width * 0.8, - minHeight: MediaQuery.of(context).size.width * 0.8, cardBuilder: (context, index) => Card( child: Image.asset('${imageList[index]}'), ), cardController: controller = CardController(), - swipeUpdateCallback: (DragUpdateDetails details, Alignment align) { + swipeUpdateCallback: (DragUpdateDetails details, Offset offset) { /// Get swiping card's alignment - if (align.x < 0) { + if (offset.dx < 0) { //Card is LEFT swiping - } else if (align.x > 0) { + } else if (offset.dx > 0) { //Card is RIGHT swiping } }, diff --git a/example/example/lib/main.dart b/example/example/lib/main.dart index 8b62c5a..6b59db1 100644 --- a/example/example/lib/main.dart +++ b/example/example/lib/main.dart @@ -47,20 +47,17 @@ class _ExampleHomePageState extends State orientation: AmassOrientation.bottom, totalNum: welcomeImages.length, stackNum: 3, - swipeEdge: 4.0, maxWidth: MediaQuery.of(context).size.width * 0.9, maxHeight: MediaQuery.of(context).size.width * 0.9, - minWidth: MediaQuery.of(context).size.width * 0.8, - minHeight: MediaQuery.of(context).size.width * 0.8, cardBuilder: (context, index) => Card( child: Image.asset('${welcomeImages[index]}'), ), cardController: controller = CardController(), - swipeUpdateCallback: (DragUpdateDetails details, Alignment align) { + swipeUpdateCallback: (DragUpdateDetails details, Offset offset) { /// Get swiping card's alignment - if (align.x < 0) { + if (offset.dx < 0) { //Card is LEFT swiping - } else if (align.x > 0) { + } else if (offset.dx > 0) { //Card is RIGHT swiping } }, diff --git a/lib/flutter_tindercard.dart b/lib/flutter_tindercard.dart index dbf059f..2769013 100644 --- a/lib/flutter_tindercard.dart +++ b/lib/flutter_tindercard.dart @@ -55,13 +55,13 @@ class TinderSwapCard extends StatefulWidget { AmassOrientation orientation = AmassOrientation.bottom, int stackNum = 3, int animDuration = 800, - double swipeEdge = 3.0, - double swipeEdgeVertical = 8.0, + double swipeEdge = 75.0, + double swipeEdgeVertical = 100.0, bool swipeUp = false, bool swipeDown = false, double maxWidth, double maxHeight, - double scaleFactor = 0.1, + double scaleFactor = 0.8, bool allowVerticalMovement = true, this.cardController, this.swipeCompleteCallback, @@ -79,31 +79,26 @@ class TinderSwapCard extends StatefulWidget { _swipeDown = swipeDown, _allowVerticalMovement = allowVerticalMovement, _cardSize = Size(maxWidth, maxHeight) { - int i = _stackNum; - while (i-- != 0) { - _cardScales.add(1 - scaleFactor * i); + double scale = 1; + double dy = 0; + for (int i = 0; i < stackNum; i++) { + _cardScales.add(scale); switch (orientation) { case AmassOrientation.top: _cardAligns.add( - Alignment( - 0.0, - (0.5 / (_stackNum - 1)) * (stackNum - i), - ), + Alignment(0.0, scale - (1 + dy)), ); break; case AmassOrientation.bottom: _cardAligns.add( - Alignment( - 0.0, - (-0.5 / (_stackNum - 1)) * (stackNum - i), - ), + Alignment(0.0, 1 + dy - scale), ); break; case AmassOrientation.left: _cardAligns.add( Alignment( - (-0.5 / (_stackNum - 1)) * (stackNum - i), + -9 * (1 + dy - scale), 0.0, ), ); @@ -111,109 +106,91 @@ class TinderSwapCard extends StatefulWidget { case AmassOrientation.right: _cardAligns.add( Alignment( - (0.5 / (_stackNum - 1)) * (stackNum - i), - 0.0, + 9 * (1 + dy - scale), + 0, ), ); break; } + + scale *= scaleFactor; + dy += .05 * scale; } } } class _TinderSwapCardState extends State with TickerProviderStateMixin { - Alignment frontCardAlign; - // Matrix4 transformation; // = Matrix4.zero(); - // Matrix4 transformation = Matrix4.translationValues(0.0, 0.0, 0.0); - // double x; - // double y; - Rect _positionRect; + Offset _dragPoint; + Offset _offset = Offset.zero; AnimationController _animationController; - int _currentFront; - static TriggerDirection _trigger; - Widget _buildCard(BuildContext context, int realIndex) { - if (realIndex < 0) { - return Container(); - } - final index = realIndex - _currentFront; - + Widget _buildCard(BuildContext context, index) { final Widget card = SizedBox.fromSize( - size: widget._cardSize, - child: widget._cardBuilder(context, widget._totalNum - realIndex - 1)); + size: widget._cardSize, child: widget._cardBuilder(context, index)); - if (index == widget._stackNum - 1) { - final angle = (pi / 180.0) * + // When user likes/dislikes by button rotate about the card centre + if (_dragPoint == null) { + _dragPoint = + Offset(widget._cardSize.width / 2, widget._cardSize.height / 2); + } + + if (index == 0) { + // Rotate about the user's finger, the opposite end has resistance + final angle = (_dragPoint.dy > widget._cardSize.height / 2 ? -1 : 1) * + .001 * (_animationController.status == AnimationStatus.forward - ? CardAnimation.frontCardRota( - _animationController, frontCardAlign.x) + ? CardAnimation.frontCardRota(_animationController, _offset.dx, + endRot: _offset.dx / 2) .value - : frontCardAlign.x); - - // final angle = 0.0; - final transformation = Matrix4.identity(); //rotationZ(angle); - transformation.rotateZ(angle * 2); - // transformation.scale(widget._cardScales[index]); - // transformation.translate(20.0, 20.0); - // transformation.scale(1.5); - - // return Transform(transform: transformation, child: card); + : _offset.dx); - Offset offset = _animationController.status == AnimationStatus.forward + _offset = _animationController.status == AnimationStatus.forward ? CardAnimation.frontCardOffset( _animationController, _offset, - Offset.zero, + Offset(MediaQuery.of(context).size.width, + MediaQuery.of(context).size.height), widget._swipeEdge, widget._swipeUp, widget._swipeDown) .value : _offset; - Rect _positionRect = Rect.fromLTWH(offset.dx * 3, offset.dy * 3, - widget._cardSize.width, widget._cardSize.height); - return Positioned.fromRect( - rect: _positionRect, - child: Transform(transform: transformation, child: card)); + final transformation = Matrix4.identity(); + transformation.translate(_offset.dx, _offset.dy); + transformation.translate(_dragPoint.dx, _dragPoint.dy); + transformation.rotateZ(angle); + transformation.translate(-_dragPoint.dx, -_dragPoint.dy); - /*return Align( - alignment: _animationController.status == AnimationStatus.forward - ? frontCardAlign = CardAnimation.frontCardAlign( - _animationController, - frontCardAlign, - widget._cardAligns[widget._stackNum - 1], - widget._swipeEdge, - widget._swipeUp, - widget._swipeDown, - ).value - : frontCardAlign, - child: Transform( - transform: transformation, - child: card, - ));*/ + return Align(child: Transform(transform: transformation, child: card)); } + final prepareNextCard = _offset.dx > widget._swipeEdge || + _offset.dx < -widget._swipeEdge || + _offset.dy > widget._swipeEdgeVertical || + _offset.dy < -widget._swipeEdgeVertical; + return Align( - alignment: _animationController.status == AnimationStatus.forward - // && (frontCardAlign.x > 3.0 || frontCardAlign.x < -3.0 || frontCardAlign.y > 3 || frontCardAlign.y < -3) + alignment: _animationController.status == AnimationStatus.forward && + prepareNextCard ? CardAnimation.backCardAlign( _animationController, widget._cardAligns[index], - widget._cardAligns[index + 1], + widget._cardAligns[index - 1], ).value : widget._cardAligns[index], child: Transform.scale( - scale: _animationController.status == AnimationStatus.forward - // && (frontCardAlign.x > 3.0 || frontCardAlign.x < -3.0 || frontCardAlign.y > 3 || frontCardAlign.y < -3) + scale: _animationController.status == AnimationStatus.forward && + prepareNextCard ? CardAnimation.backCardScale( _animationController, widget._cardScales[index], - widget._cardScales[index + 1], + widget._cardScales[index - 1], ).value : widget._cardScales[index], child: card, @@ -223,58 +200,32 @@ class _TinderSwapCardState extends State List _buildCards(BuildContext context) { final cards = []; - final size = MediaQuery.of(context).size; - // transformation = Matrix4.translationValues(0.0, 0.0, 0.0); - // transformation = Matrix4.translationValues(50.0, 50.0, 0.0); - // _positionRect = Rect.fromLTRB(0, 0, size.width, size.height); - - for (var i = _currentFront; i < _currentFront + widget._stackNum; i++) { + int i = min(widget._stackNum, widget._totalNum); + while (i-- != 0) { cards.add(_buildCard(context, i)); } cards.add(SizedBox.expand( child: GestureDetector( - onPanStart: (DragStartDetails details) {}, + onPanStart: (DragStartDetails details) { + _dragPoint = details.localPosition; + }, onPanUpdate: (final details) { - // print('onPanUpdate: ${frontCardAlign.x} + ${details.delta.dx} * 20 / ${MediaQuery.of(context).size.width} ' - // '= ${frontCardAlign.x + details.delta.dx * 20 / MediaQuery.of(context).size.width}'); - // print('onPanUpdate: ${details.globalPosition}'); - setState(() { if (widget._allowVerticalMovement == true) { - frontCardAlign += Alignment( - 10 * details.delta.dx / (size.width / 2), - 15 * details.delta.dy / (size.height / 2), - ); - - // frontCardAlign = Alignment( - // frontCardAlign.x + details.delta.dx * 20 / MediaQuery.of(context).size.width, - // frontCardAlign.y + details.delta.dy * 30 / MediaQuery.of(context).size.height, - // ); - - // transformation.translate(-details.delta.dx * 10, details.delta.dy * 15); - // transformation = Matrix4.translationValues(details.delta.dx * 10, details.delta.dy * 15, 0.0); - // transformation.s - // _positionRect = Rect.fromPoints( - // details.globalPosition, details.globalPosition); - _positionRect = _positionRect.shift(details.delta * 100.0); _offset += details.delta; - // print('Positioned: $_positionRect'); + // print('offset: $_offset'); } else { - frontCardAlign = Alignment( - frontCardAlign.x + - details.delta.dx * 20 / MediaQuery.of(context).size.width, - 0, - ); + _offset = Offset(_offset.dx + details.delta.dx, 0); } if (widget.swipeUpdateCallback != null) { - widget.swipeUpdateCallback(details, frontCardAlign); + widget.swipeUpdateCallback(details, _offset); } }); }, - onPanEnd: (final details) { + onPanEnd: (final DragEndDetails details) { animateCards(TriggerDirection.none); }, ), @@ -283,8 +234,7 @@ class _TinderSwapCardState extends State } void animateCards(TriggerDirection trigger) { - if (_animationController.isAnimating || - _currentFront + widget._stackNum == 0) { + if (_animationController.isAnimating) { return; } _trigger = trigger; @@ -297,7 +247,7 @@ class _TinderSwapCardState extends State animateCards(trigger); } - // support for asynchronous data events + /// support for asynchronous data events @override void didUpdateWidget(covariant TinderSwapCard oldWidget) { super.didUpdateWidget(oldWidget); @@ -319,15 +269,7 @@ class _TinderSwapCardState extends State } void _initState() { - // print('------------------- initialise transformation 0,0,0'); - // transformation = Matrix4.translationValues(0.0, 0.0, 0.0); _offset = Offset.zero; - _positionRect = Rect.fromLTRB( - 0.0, 0.0, widget._cardSize.width, widget._cardSize.height); - - _currentFront = widget._totalNum - widget._stackNum; - - frontCardAlign = widget._cardAligns[widget._cardAligns.length - 1]; _animationController = AnimationController( vsync: this, @@ -340,29 +282,24 @@ class _TinderSwapCardState extends State _animationController.addStatusListener( (final status) { - final index = widget._totalNum - widget._stackNum - _currentFront; - if (status == AnimationStatus.completed) { CardSwipeOrientation orientation; - if (frontCardAlign.x < -widget._swipeEdge) { + if (_offset.dx < -widget._swipeEdge) { orientation = CardSwipeOrientation.left; - } else if (frontCardAlign.x > widget._swipeEdge) { + } else if (_offset.dx > widget._swipeEdge) { orientation = CardSwipeOrientation.right; - } else if (frontCardAlign.y < -widget._swipeEdgeVertical) { + } else if (_offset.dy < -widget._swipeEdgeVertical) { orientation = CardSwipeOrientation.up; - } else if (frontCardAlign.y > widget._swipeEdgeVertical) { + } else if (_offset.dy > widget._swipeEdgeVertical) { orientation = CardSwipeOrientation.down; } else { - frontCardAlign = widget._cardAligns[widget._stackNum - 1]; orientation = CardSwipeOrientation.recover; } if (widget.swipeCompleteCallback != null) { - widget.swipeCompleteCallback(orientation, index); + widget.swipeCompleteCallback(orientation); } - - if (orientation != CardSwipeOrientation.recover) changeCardOrder(); } }, ); @@ -372,17 +309,7 @@ class _TinderSwapCardState extends State Widget build(BuildContext context) { widget.cardController?.addListener(triggerSwap); - return Stack( - // overflow: , - clipBehavior: Clip.none, - children: _buildCards(context)); - } - - void changeCardOrder() { - setState(() { - _currentFront--; - frontCardAlign = widget._cardAligns[widget._stackNum - 1]; - }); + return Stack(clipBehavior: Clip.none, children: _buildCards(context)); } } @@ -393,114 +320,52 @@ enum CardSwipeOrientation { left, right, recover, up, down } /// swipe card to [CardSwipeOrientation.left] or [CardSwipeOrientation.right] /// , [CardSwipeOrientation.recover] means back to start. typedef CardSwipeCompleteCallback = void Function( - CardSwipeOrientation orientation, int index); + CardSwipeOrientation orientation); /// [DragUpdateDetails] of swiping card. typedef CardDragUpdateCallback = void Function( - DragUpdateDetails details, Alignment align); + DragUpdateDetails details, Offset offset); enum AmassOrientation { top, bottom, left, right } class CardAnimation { - /*static Animation frontCardAlign( - AnimationController controller, - Alignment beginAlign, - Alignment baseAlign, - double swipeEdge, - bool swipeUp, - bool swipeDown, - ) { - double endX, endY; - - if (_TinderSwapCardState._trigger == TriggerDirection.none) { - // onPanEnd - endX = beginAlign.x > 0 - ? (beginAlign.x > swipeEdge ? beginAlign.x + 10.0 : baseAlign.x) - : (beginAlign.x < -swipeEdge ? beginAlign.x - 10.0 : baseAlign.x); - endY = beginAlign.x > 3.0 || beginAlign.x < -swipeEdge - ? beginAlign.y - : baseAlign.y; - - if (swipeUp || swipeDown) { - if (beginAlign.y < 0) { - if (swipeUp) { - endY = - beginAlign.y < -swipeEdge ? beginAlign.y - 10.0 : baseAlign.y; - } - } else if (beginAlign.y > 0) { - if (swipeDown) { - endY = beginAlign.y > swipeEdge ? beginAlign.y + 10.0 : baseAlign.y; - } - } - } - } else if (_TinderSwapCardState._trigger == TriggerDirection.left) { - endX = beginAlign.x - swipeEdge; - endY = beginAlign.y + 0.5; - } - */ /* Trigger Swipe Up or Down */ /* - else if (_TinderSwapCardState._trigger == TriggerDirection.up || - _TinderSwapCardState._trigger == TriggerDirection.down) { - var beginY = - _TinderSwapCardState._trigger == TriggerDirection.up ? -10 : 10; - - endY = beginY < -swipeEdge ? beginY - 10.0 : baseAlign.y; - - endX = beginAlign.x > 0 - ? (beginAlign.x > swipeEdge ? beginAlign.x + 10.0 : baseAlign.x) - : (beginAlign.x < -swipeEdge ? beginAlign.x - 10.0 : baseAlign.x); - } else { - endX = beginAlign.x + swipeEdge; - endY = beginAlign.y + 0.5; - } - return AlignmentTween( - begin: beginAlign, - end: Alignment(endX, endY), - ).animate( - CurvedAnimation( - parent: controller, - curve: Curves.easeOut, - ), - ); - }*/ - static Animation frontCardOffset( AnimationController controller, Offset beginOffset, - Offset baseOffset, + Offset screenOffset, double swipeEdge, bool swipeUp, bool swipeDown, ) { double endX, endY; + // onPanEnd if (_TinderSwapCardState._trigger == TriggerDirection.none) { - // onPanEnd + // need to multiply screenOffset.dx so that the trailing corner doesn't hang around endX = beginOffset.dx > 0 - ? (beginOffset.dx > swipeEdge ? beginOffset.dx + 10.0 : baseOffset.dx) - : (beginOffset.dx < -swipeEdge - ? beginOffset.dx - 10.0 - : baseOffset.dx); - endY = beginOffset.dx > 3.0 || beginOffset.dx < -swipeEdge + ? (beginOffset.dx > swipeEdge ? screenOffset.dx * 1.5 : 0) + : (beginOffset.dx < -swipeEdge ? -screenOffset.dx * 1.5 : 0); + endY = beginOffset.dx > swipeEdge || beginOffset.dx < -swipeEdge ? beginOffset.dy - : baseOffset.dy; + : 0; if (swipeUp || swipeDown) { if (beginOffset.dy < 0) { if (swipeUp) { endY = beginOffset.dy < -swipeEdge - ? beginOffset.dy - 10.0 - : baseOffset.dy; + ? beginOffset.dy - screenOffset.dy + : 0; } } else if (beginOffset.dy > 0) { if (swipeDown) { endY = beginOffset.dy > swipeEdge - ? beginOffset.dy + 10.0 - : baseOffset.dy; + ? beginOffset.dy + screenOffset.dy + : 0; } } } } else if (_TinderSwapCardState._trigger == TriggerDirection.left) { - endX = beginOffset.dx - swipeEdge; + endX = -screenOffset.dx * 1.5; endY = beginOffset.dy + 0.5; } /* Trigger Swipe Up or Down */ @@ -509,20 +374,20 @@ class CardAnimation { var beginY = _TinderSwapCardState._trigger == TriggerDirection.up ? -10 : 10; - endY = beginY < -swipeEdge ? beginY - 10.0 : baseOffset.dy; + endY = beginY < -swipeEdge ? beginY - 10.0 : screenOffset.dy; endX = beginOffset.dx > 0 - ? (beginOffset.dx > swipeEdge ? beginOffset.dx + 10.0 : baseOffset.dx) + ? (beginOffset.dx > swipeEdge + ? beginOffset.dx + 10.0 + : screenOffset.dx) : (beginOffset.dx < -swipeEdge ? beginOffset.dx - 10.0 - : baseOffset.dx); + : screenOffset.dx); } else { - endX = beginOffset.dx + swipeEdge; + endX = screenOffset.dx * 1.5; endY = beginOffset.dy + 0.5; } - // print('endX: $endX, begin.x: ${beginOffset.dx}, swipeEdge: $swipeEdge'); - return Tween( begin: beginOffset, end: Offset(endX, endY), @@ -535,8 +400,9 @@ class CardAnimation { } static Animation frontCardRota( - AnimationController controller, double beginRot) { - return Tween(begin: beginRot, end: 0.0).animate( + AnimationController controller, double beginRot, + {double endRot = 0.0}) { + return Tween(begin: beginRot, end: endRot).animate( CurvedAnimation( parent: controller, curve: Curves.easeOut,