feat(cards): render the items through a builder callback

This commit is contained in:
ricardodalarme 2023-03-18 17:02:52 -03:00
parent 5edd21e371
commit db500bf458
7 changed files with 72 additions and 51 deletions

View File

@ -1,7 +1,8 @@
## NEXT ## [3.0.0]
- **BREAKING CHANGES**: - **BREAKING CHANGES**:
- Add currentIndex and previousIndex to the onSwipe callback - Add currentIndex and previousIndex to the onSwipe callback
- Render the items through a builder function
## [2.1.0] ## [2.1.0]

View File

@ -81,7 +81,8 @@ class Example extends StatelessWidget {
return Scaffold( return Scaffold(
body: Flexible( body: Flexible(
child: CardSwiper( child: CardSwiper(
cards: cards, cardsCount: cards.length,
cardBuilder: (context, index) => cards[index],
), ),
), ),
); );
@ -92,10 +93,10 @@ class Example extends StatelessWidget {
## Constructor ## Constructor
#### Basic #### Basic
| Parameter | Default | Description | Required | | 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 | controller | - | Trigger swipe | false
| padding | EdgeInsets.symmetric(horizontal: 20, vertical: 25) | Control swiper padding | false | padding | EdgeInsets.symmetric(horizontal: 20, vertical: 25) | Control swiper padding | false
| duration | 200 milliseconds | The duration that every animation should last | false | duration | 200 milliseconds | The duration that every animation should last | false

View File

@ -35,10 +35,11 @@ class _ExamplePageState extends State<Example> {
Flexible( Flexible(
child: CardSwiper( child: CardSwiper(
controller: controller, controller: controller,
cards: cards, cardsCount: cards.length,
numberOfCardsDisplayed: 3, numberOfCardsDisplayed: 3,
onSwipe: _swipe, onSwipe: _onSwipe,
padding: const EdgeInsets.all(24.0), padding: const EdgeInsets.all(24.0),
cardBuilder: (context, index) => cards[index],
), ),
), ),
Padding( Padding(
@ -68,16 +69,16 @@ class _ExamplePageState extends State<Example> {
), ),
], ],
), ),
) ),
], ],
), ),
), ),
); );
} }
void _swipe( void _onSwipe(
int previousIndex, int? previousIndex,
int currentIndex, int? currentIndex,
CardSwiperDirection direction, CardSwiperDirection direction,
) { ) {
debugPrint( debugPrint(

View File

@ -3,11 +3,15 @@ import 'dart:math';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_card_swiper/src/card_swiper_controller.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/enums.dart';
import 'package:flutter_card_swiper/src/extensions.dart';
import 'package:flutter_card_swiper/src/typedefs.dart'; import 'package:flutter_card_swiper/src/typedefs.dart';
class CardSwiper<T extends Widget> extends StatefulWidget { class CardSwiper<T extends Widget> extends StatefulWidget {
/// list of widgets for the swiper /// widget builder for rendering cards
final List<T> cards; final NullableIndexedWidgetBuilder cardBuilder;
/// cards count
final int cardsCount;
/// controller to trigger actions /// controller to trigger actions
final CardSwiperController? controller; final CardSwiperController? controller;
@ -56,7 +60,8 @@ class CardSwiper<T extends Widget> extends StatefulWidget {
const CardSwiper({ const CardSwiper({
Key? key, Key? key,
required this.cards, required this.cardBuilder,
required this.cardsCount,
this.controller, this.controller,
this.padding = const EdgeInsets.symmetric(horizontal: 20, vertical: 25), this.padding = const EdgeInsets.symmetric(horizontal: 20, vertical: 25),
this.duration = const Duration(milliseconds: 200), this.duration = const Duration(milliseconds: 200),
@ -89,7 +94,7 @@ class CardSwiper<T extends Widget> extends StatefulWidget {
'scale must be between 0 and 1', 'scale must be between 0 and 1',
), ),
assert( 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', 'you must display at least one card, and no more than the length of cards parameter',
), ),
super(key: key); super(key: key);
@ -116,21 +121,18 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper<T>>
late Animation<double> _scaleAnimation; late Animation<double> _scaleAnimation;
late Animation<double> _differenceAnimation; late Animation<double> _differenceAnimation;
final List<T> _stack = [];
CardSwiperDirection detectedDirection = CardSwiperDirection.none; CardSwiperDirection detectedDirection = CardSwiperDirection.none;
double get _maxAngle => widget.maxAngle * (pi / 180); double get _maxAngle => widget.maxAngle * (pi / 180);
int get _currentIndex => _stack.length - 1; int? _currentIndex = 0;
bool get _canSwipe => _stack.isNotEmpty && !widget.isDisabled; int? get _nextIndex => getValidIndexOffset(1);
bool get _canSwipe => _currentIndex != null && !widget.isDisabled;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_stack.addAll(widget.cards);
widget.controller?.addListener(_controllerListener); widget.controller?.addListener(_controllerListener);
_animationController = AnimationController( _animationController = AnimationController(
@ -176,18 +178,6 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper<T>>
); );
} }
///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 /// The card shown at the front of the stack, that can be dragged and swipped
Widget _frontItem(BoxConstraints constraints) { Widget _frontItem(BoxConstraints constraints) {
return Positioned( return Positioned(
@ -198,7 +188,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper<T>>
angle: _angle, angle: _angle,
child: ConstrainedBox( child: ConstrainedBox(
constraints: constraints, constraints: constraints,
child: _stack[_currentIndex], child: widget.cardBuilder(context, _currentIndex!),
), ),
), ),
onTap: () { onTap: () {
@ -251,10 +241,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper<T>>
scale: _scale, scale: _scale,
child: ConstrainedBox( child: ConstrainedBox(
constraints: constraints, constraints: constraints,
child: _stack.length <= 1 child: widget.cardBuilder(context, _nextIndex!),
? widget.cards.last
: _stack[_currentIndex - 1],
//or: widget.cards[(_currentIndex - 1) % widget.cards.length] (same thing)
), ),
), ),
); );
@ -262,7 +249,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper<T>>
/// if widget.numberOfCardsDisplayed > 2, those cards are built behind the /// if widget.numberOfCardsDisplayed > 2, those cards are built behind the
/// _secondItem and can't move at all /// _secondItem and can't move at all
Widget _backItem(BoxConstraints constraints, int index) { Widget _backItem(BoxConstraints constraints, int offset) {
return Positioned( return Positioned(
top: 40, top: 40,
left: 0, left: 0,
@ -270,7 +257,10 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper<T>>
scale: widget.scale, scale: widget.scale,
child: ConstrainedBox( child: ConstrainedBox(
constraints: constraints, constraints: constraints,
child: widget.cards[(_currentIndex - index) % widget.cards.length], child: widget.cardBuilder(
context,
getValidIndexOffset(offset)!,
),
), ),
), ),
); );
@ -317,21 +307,17 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper<T>>
setState(() { setState(() {
if (_swipeType == SwipeType.swipe) { if (_swipeType == SwipeType.swipe) {
final previousIndex = _currentIndex; final previousIndex = _currentIndex;
_stack.removeAt(_currentIndex); final isLastCard = _currentIndex == widget.cardsCount - 1;
_currentIndex = _nextIndex;
widget.onSwipe?.call( widget.onSwipe?.call(
previousIndex, previousIndex,
widget.isLoop && _stack.isEmpty _currentIndex,
? widget.cards.length - 1
: _currentIndex,
detectedDirection, detectedDirection,
); );
if (_stack.isEmpty) { if (isLastCard) {
widget.onEnd?.call(); widget.onEnd?.call();
if (widget.isLoop) {
_stack.addAll(widget.cards);
}
} }
} }
_animationController.reset(); _animationController.reset();
@ -486,4 +472,31 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper<T>>
_swipeType = SwipeType.back; _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;
}
} }

5
lib/src/extensions.dart Normal file
View File

@ -0,0 +1,5 @@
extension Range on num {
bool isBetween(num from, num to) {
return from < this && this < to;
}
}

View File

@ -1,8 +1,8 @@
import 'package:flutter_card_swiper/src/enums.dart'; import 'package:flutter_card_swiper/src/enums.dart';
typedef CardSwiperOnSwipe = void Function( typedef CardSwiperOnSwipe = void Function(
int previousIndex, int? previousIndex,
int currentIndex, int? currentIndex,
CardSwiperDirection direction, CardSwiperDirection direction,
); );

View File

@ -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. 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 homepage: https://github.com/ricardodalarme/flutter_card_swiper
issue_tracker: https://github.com/ricardodalarme/flutter_card_swiper/issues issue_tracker: https://github.com/ricardodalarme/flutter_card_swiper/issues
version: 2.1.0 version: 3.0.0
environment: environment:
sdk: ">=2.12.0 <3.0.0" sdk: ">=2.12.0 <3.0.0"