From db500bf4588b3c66522a3b979c3d555ae16ca3cb Mon Sep 17 00:00:00 2001 From: ricardodalarme Date: Sat, 18 Mar 2023 17:02:52 -0300 Subject: [PATCH] feat(cards): render the items through a builder callback --- CHANGELOG.md | 3 +- README.md | 7 ++-- example/lib/main.dart | 13 +++--- lib/src/card_swiper.dart | 89 +++++++++++++++++++++++----------------- lib/src/extensions.dart | 5 +++ lib/src/typedefs.dart | 4 +- pubspec.yaml | 2 +- 7 files changed, 72 insertions(+), 51 deletions(-) create mode 100644 lib/src/extensions.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index bd37721..29b9d9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## [3.0.0] - **BREAKING CHANGES**: - Add currentIndex and previousIndex to the onSwipe callback + - Render the items through a builder function ## [2.1.0] diff --git a/README.md b/README.md index fad9d5f..7164c22 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,8 @@ class Example extends StatelessWidget { return Scaffold( body: Flexible( child: CardSwiper( - cards: cards, + cardsCount: cards.length, + cardBuilder: (context, index) => cards[index], ), ), ); @@ -92,10 +93,10 @@ class Example extends StatelessWidget { ## Constructor #### Basic - | Parameter | Default | Description | Required | | ------------- |:-------------|:-----|:-----:| -| cards | - | List of Widgets for the swiper | true +| cardBuilder | - | Widget builder for rendering cards | true +| cardsCount | - | Cards count | true | controller | - | Trigger swipe | false | padding | EdgeInsets.symmetric(horizontal: 20, vertical: 25) | Control swiper padding | false | duration | 200 milliseconds | The duration that every animation should last | false diff --git a/example/lib/main.dart b/example/lib/main.dart index 473ab72..bdcbc00 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -35,10 +35,11 @@ class _ExamplePageState extends State { Flexible( child: CardSwiper( controller: controller, - cards: cards, + cardsCount: cards.length, numberOfCardsDisplayed: 3, - onSwipe: _swipe, + onSwipe: _onSwipe, padding: const EdgeInsets.all(24.0), + cardBuilder: (context, index) => cards[index], ), ), Padding( @@ -68,16 +69,16 @@ class _ExamplePageState extends State { ), ], ), - ) + ), ], ), ), ); } - void _swipe( - int previousIndex, - int currentIndex, + void _onSwipe( + int? previousIndex, + int? currentIndex, CardSwiperDirection direction, ) { debugPrint( diff --git a/lib/src/card_swiper.dart b/lib/src/card_swiper.dart index ab6933a..9ff05d7 100644 --- a/lib/src/card_swiper.dart +++ b/lib/src/card_swiper.dart @@ -3,11 +3,15 @@ import 'dart:math'; import 'package:flutter/widgets.dart'; import 'package:flutter_card_swiper/src/card_swiper_controller.dart'; import 'package:flutter_card_swiper/src/enums.dart'; +import 'package:flutter_card_swiper/src/extensions.dart'; import 'package:flutter_card_swiper/src/typedefs.dart'; class CardSwiper extends StatefulWidget { - /// list of widgets for the swiper - final List cards; + /// widget builder for rendering cards + final NullableIndexedWidgetBuilder cardBuilder; + + /// cards count + final int cardsCount; /// controller to trigger actions final CardSwiperController? controller; @@ -56,7 +60,8 @@ class CardSwiper extends StatefulWidget { const CardSwiper({ Key? key, - required this.cards, + required this.cardBuilder, + required this.cardsCount, this.controller, this.padding = const EdgeInsets.symmetric(horizontal: 20, vertical: 25), this.duration = const Duration(milliseconds: 200), @@ -89,7 +94,7 @@ class CardSwiper extends StatefulWidget { 'scale must be between 0 and 1', ), assert( - numberOfCardsDisplayed >= 1 && numberOfCardsDisplayed <= cards.length, + numberOfCardsDisplayed >= 1 && numberOfCardsDisplayed <= cardsCount, 'you must display at least one card, and no more than the length of cards parameter', ), super(key: key); @@ -116,21 +121,18 @@ class _CardSwiperState extends State> late Animation _scaleAnimation; late Animation _differenceAnimation; - final List _stack = []; - CardSwiperDirection detectedDirection = CardSwiperDirection.none; double get _maxAngle => widget.maxAngle * (pi / 180); - int get _currentIndex => _stack.length - 1; - bool get _canSwipe => _stack.isNotEmpty && !widget.isDisabled; + int? _currentIndex = 0; + int? get _nextIndex => getValidIndexOffset(1); + bool get _canSwipe => _currentIndex != null && !widget.isDisabled; @override void initState() { super.initState(); - _stack.addAll(widget.cards); - widget.controller?.addListener(_controllerListener); _animationController = AnimationController( @@ -176,18 +178,6 @@ 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( @@ -198,7 +188,7 @@ class _CardSwiperState extends State> angle: _angle, child: ConstrainedBox( constraints: constraints, - child: _stack[_currentIndex], + child: widget.cardBuilder(context, _currentIndex!), ), ), onTap: () { @@ -251,10 +241,7 @@ class _CardSwiperState extends State> scale: _scale, child: ConstrainedBox( constraints: constraints, - child: _stack.length <= 1 - ? widget.cards.last - : _stack[_currentIndex - 1], - //or: widget.cards[(_currentIndex - 1) % widget.cards.length] (same thing) + child: widget.cardBuilder(context, _nextIndex!), ), ), ); @@ -262,7 +249,7 @@ class _CardSwiperState extends State> /// if widget.numberOfCardsDisplayed > 2, those cards are built behind the /// _secondItem and can't move at all - Widget _backItem(BoxConstraints constraints, int index) { + Widget _backItem(BoxConstraints constraints, int offset) { return Positioned( top: 40, left: 0, @@ -270,7 +257,10 @@ class _CardSwiperState extends State> scale: widget.scale, child: ConstrainedBox( constraints: constraints, - child: widget.cards[(_currentIndex - index) % widget.cards.length], + child: widget.cardBuilder( + context, + getValidIndexOffset(offset)!, + ), ), ), ); @@ -317,21 +307,17 @@ class _CardSwiperState extends State> setState(() { if (_swipeType == SwipeType.swipe) { final previousIndex = _currentIndex; - _stack.removeAt(_currentIndex); + final isLastCard = _currentIndex == widget.cardsCount - 1; + + _currentIndex = _nextIndex; widget.onSwipe?.call( previousIndex, - widget.isLoop && _stack.isEmpty - ? widget.cards.length - 1 - : _currentIndex, + _currentIndex, detectedDirection, ); - if (_stack.isEmpty) { + if (isLastCard) { widget.onEnd?.call(); - - if (widget.isLoop) { - _stack.addAll(widget.cards); - } } } _animationController.reset(); @@ -486,4 +472,31 @@ class _CardSwiperState extends State> _swipeType = SwipeType.back; } + + ///the number of cards that are built on the screen + int nbOfCardsOnScreen() { + if (widget.isLoop) { + return widget.numberOfCardsDisplayed; + } + if (_currentIndex == null) { + return 0; + } + + return min( + widget.numberOfCardsDisplayed, + widget.cardsCount - _currentIndex!, + ); + } + + int? getValidIndexOffset(int offset) { + if (_currentIndex == null) { + return null; + } + + final index = _currentIndex! + offset; + if (!widget.isLoop && !index.isBetween(0, widget.cardsCount)) { + return null; + } + return index % widget.cardsCount; + } } diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart new file mode 100644 index 0000000..37598e6 --- /dev/null +++ b/lib/src/extensions.dart @@ -0,0 +1,5 @@ +extension Range on num { + bool isBetween(num from, num to) { + return from < this && this < to; + } +} diff --git a/lib/src/typedefs.dart b/lib/src/typedefs.dart index 6426e62..9de9980 100644 --- a/lib/src/typedefs.dart +++ b/lib/src/typedefs.dart @@ -1,8 +1,8 @@ import 'package:flutter_card_swiper/src/enums.dart'; typedef CardSwiperOnSwipe = void Function( - int previousIndex, - int currentIndex, + int? previousIndex, + int? currentIndex, CardSwiperDirection direction, ); diff --git a/pubspec.yaml b/pubspec.yaml index 9ef24ce..9aa1bc6 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.1.0 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0"