From a8cbd3a1109f95f09197b0e115f81e2cb73b8673 Mon Sep 17 00:00:00 2001 From: giboin <81298073+giboin@users.noreply.github.com> Date: Sat, 18 Mar 2023 18:43:24 +0100 Subject: [PATCH] feature: display more cards behind the first one (#6) --- CHANGELOG.md | 4 +++ README.md | 1 + example/lib/main.dart | 1 + lib/src/card_swiper.dart | 56 +++++++++++++++++++++++++++++++++++----- pubspec.yaml | 2 +- 5 files changed, 57 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c6bf5f..cc98711 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [2.1.0] + +- Add option to display more cards at a time. Useful if the widgets you want in your cards take time to build (for example a network image or video): displaying more cards builds them in advance and makes a fast serie of swipes more fluid. + ## [2.0.1] - Fixes wrong item rendering. diff --git a/README.md b/README.md index 464806e..fad9d5f 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ class Example extends StatelessWidget { | onSwipe | - | Called with the new index and detected swipe direction when the user swiped | false | onEnd | - | Called when there is no Widget left to be swiped away | false | direction | right | Direction in which the card is swiped away when triggered from the outside | false +| numberOfCardsDisplayed | 2 | If your widgets in the 'cards' list cause performance issues, you can choose to display more cards at a time to reduce how long the user waits for a card to appear | false #### Controller diff --git a/example/lib/main.dart b/example/lib/main.dart index d2a6c2e..58189a1 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -36,6 +36,7 @@ class _ExamplePageState extends State { child: CardSwiper( controller: controller, cards: cards, + numberOfCardsDisplayed: 3, onSwipe: _swipe, padding: const EdgeInsets.all(24.0), ), diff --git a/lib/src/card_swiper.dart b/lib/src/card_swiper.dart index d6f41ba..6472692 100644 --- a/lib/src/card_swiper.dart +++ b/lib/src/card_swiper.dart @@ -51,6 +51,9 @@ class CardSwiper extends StatefulWidget { /// set to true if the stack should loop final bool isLoop; + /// here you can change the number of cards that are displayed at the same time + final int numberOfCardsDisplayed; + const CardSwiper({ Key? key, required this.cards, @@ -68,6 +71,7 @@ class CardSwiper extends StatefulWidget { this.isHorizontalSwipingEnabled = true, this.isVerticalSwipingEnabled = true, this.isLoop = true, + this.numberOfCardsDisplayed = 2, }) : assert( maxAngle >= 0 && maxAngle <= 360, 'maxAngle must be between 0 and 360', @@ -84,6 +88,10 @@ class CardSwiper extends StatefulWidget { scale >= 0 && scale <= 1, 'scale must be between 0 and 1', ), + assert( + numberOfCardsDisplayed >= 1 && numberOfCardsDisplayed <= cards.length, + 'you must display at least one card, and no more than the length of cards parameter', + ), super(key: key); @override @@ -116,7 +124,6 @@ class _CardSwiperState extends State> int get _currentIndex => _stack.length - 1; bool get _canSwipe => _stack.isNotEmpty && !widget.isDisabled; - bool get _hasBackItem => _stack.length > 1 || widget.isLoop; @override void initState() { @@ -152,10 +159,15 @@ class _CardSwiperState extends State> return Stack( clipBehavior: Clip.none, fit: StackFit.expand, - children: [ - if (_hasBackItem) _backItem(constraints), - if (_stack.isNotEmpty) _frontItem(constraints), - ], + children: List.generate(nbOfCardsOnScreen(), (index) { + if (index == 0) { + return _frontItem(constraints); + } + if (index == 1) { + return _secondItem(constraints); + } + return _backItem(constraints, index); + }).reversed.toList(), ); }, ), @@ -164,6 +176,19 @@ class _CardSwiperState extends State> ); } + ///the number of cards that are built on the screen + int nbOfCardsOnScreen() { + return widget.isLoop + ? widget.numberOfCardsDisplayed + : _stack.isNotEmpty + ? min( + widget.numberOfCardsDisplayed, + _stack.length, + ) + : 0; + } + + /// The card shown at the front of the stack, that can be dragged and swipped Widget _frontItem(BoxConstraints constraints) { return Positioned( left: _left, @@ -216,7 +241,9 @@ class _CardSwiperState extends State> ); } - Widget _backItem(BoxConstraints constraints) { + /// the card that is just behind the _frontItem, only moves to take its place + /// during a movement of _frontItem + Widget _secondItem(BoxConstraints constraints) { return Positioned( top: _difference, left: 0, @@ -227,6 +254,23 @@ class _CardSwiperState extends State> child: _stack.length <= 1 ? widget.cards.last : _stack[_currentIndex - 1], + //or: widget.cards[(_currentIndex - 1) % widget.cards.length] (same thing) + ), + ), + ); + } + + /// if widget.numberOfCardsDisplayed > 2, those cards are built behind the + /// _secondItem and can't move at all + Widget _backItem(BoxConstraints constraints, int index) { + return Positioned( + top: 40, + left: 0, + child: Transform.scale( + scale: widget.scale, + child: ConstrainedBox( + constraints: constraints, + child: widget.cards[(_currentIndex - index) % widget.cards.length], ), ), ); diff --git a/pubspec.yaml b/pubspec.yaml index c8ed9ff..9ef24ce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_card_swiper description: This is a Tinder-like card swiper package. It allows you to swipe left, right, up, and down and define your own business logic for each direction. homepage: https://github.com/ricardodalarme/flutter_card_swiper issue_tracker: https://github.com/ricardodalarme/flutter_card_swiper/issues -version: 2.0.1 +version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0"