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**:
- Add currentIndex and previousIndex to the onSwipe callback
- Render the items through a builder function
## [2.1.0]

View File

@ -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

View File

@ -35,10 +35,11 @@ class _ExamplePageState extends State<Example> {
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<Example> {
),
],
),
)
),
],
),
),
);
}
void _swipe(
int previousIndex,
int currentIndex,
void _onSwipe(
int? previousIndex,
int? currentIndex,
CardSwiperDirection direction,
) {
debugPrint(

View File

@ -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<T extends Widget> extends StatefulWidget {
/// list of widgets for the swiper
final List<T> 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<T extends Widget> 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<T extends Widget> 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<T extends Widget> extends State<CardSwiper<T>>
late Animation<double> _scaleAnimation;
late Animation<double> _differenceAnimation;
final List<T> _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<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
Widget _frontItem(BoxConstraints constraints) {
return Positioned(
@ -198,7 +188,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper<T>>
angle: _angle,
child: ConstrainedBox(
constraints: constraints,
child: _stack[_currentIndex],
child: widget.cardBuilder(context, _currentIndex!),
),
),
onTap: () {
@ -251,10 +241,7 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper<T>>
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<T extends Widget> extends State<CardSwiper<T>>
/// 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<T extends Widget> extends State<CardSwiper<T>>
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<T extends Widget> extends State<CardSwiper<T>>
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<T extends Widget> extends State<CardSwiper<T>>
_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';
typedef CardSwiperOnSwipe = void Function(
int previousIndex,
int currentIndex,
int? previousIndex,
int? currentIndex,
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.
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"