initial commit

This commit is contained in:
Jorian van der Kolk 2022-09-02 14:00:16 +02:00
parent e260f49c1e
commit 3fa92aa849
8 changed files with 171 additions and 128 deletions

View file

@ -1,2 +1,27 @@
# carousel
card carousel widget
# Carousel
Carousel widget. Makes it easier to create card carousels using a list of transforms.
Each card can change its rotation, position and scale when swiping the cards.
Supports all platforms.
![Demo video](demo.gif)
## Usage
To use this package, add `carousel` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels).
### Example
See [Example Code](example/lib/main.dart) for more info.
## Issues
Please file any issues, bugs or feature request as an issue on our [GitHub](https://github.com/Iconica-Development/carousel) page. Commercial support is available if you need help with integration with your app or services. You can contact us at [support@iconica.nl](mailto:support@iconica.nl).
## Want to contribute
If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](./CONTRIBUTING.md) and send us your [pull request](https://github.com/Iconica-Development/carousel/pulls).
## Author
This carousel for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl>

BIN
demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 MiB

View file

@ -1,5 +1,5 @@
import 'package:carousel/carousel.dart';
import 'package:carousel/models/card_transform.dart';
import 'package:carousel_example/pokemon.dart';
import 'package:carousel_example/pokemon_card.dart';
import 'package:flutter/material.dart';

View file

@ -1,125 +1,4 @@
library carousel;
export 'package:carousel/carousel.dart';
import 'package:carousel/models/card_transform.dart';
import 'package:carousel/widgets/carousel_card.dart';
import 'package:flutter/material.dart';
typedef CarouselCardBuilder = Widget Function(BuildContext context, int index);
class Carousel extends StatefulWidget {
const Carousel({
required this.transforms,
required this.builder,
required this.selectableCardId,
this.pageViewHeight = 300,
this.onPageChanged,
this.alignment = AlignmentDirectional.topStart,
this.onCardClick,
Key? key,
}) : super(key: key);
/// A list of transforms to calculate the position of the card when swiping.
/// Every item in the list is one of the possible card positions.
final List<CardTransform> transforms;
/// The index of the transform card which acts as the selected card.
final int selectableCardId;
/// Builder for the card given a [context] and a [index] to identify the right card.
final CarouselCardBuilder builder;
/// Called when selected card is changed to the next one.
final void Function(int value)? onPageChanged;
// Callen when selected card is clicked.
final void Function(int value)? onCardClick;
/// Alignment of the cards.
final AlignmentGeometry alignment;
/// Size of the pageview used to capture swipe gestures.
final double pageViewHeight;
@override
State<Carousel> createState() => _CarouselState();
}
class _CarouselState extends State<Carousel> {
final PageController _pageController = PageController(initialPage: 0);
double _currentPage = 0;
@override
void initState() {
_pageController.addListener(() {
_currentPage = _pageController.page!;
});
super.initState();
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
alignment: widget.alignment,
children: [
AnimatedBuilder(
animation: _pageController,
builder: (context, _) {
final transitionPos = _currentPage % 1;
final index = _currentPage.floor();
final length = widget.transforms.length - 1;
return Stack(
children: [
for (var i = 0; i < length; i++) ...[
CarouselCard(
cardTransform: widget.transforms[i]
.transform(widget.transforms[i + 1], transitionPos),
child: widget.builder.call(context, index - i),
),
],
],
);
},
),
SizedBox(
height: widget.pageViewHeight,
child: PageView.builder(
onPageChanged: widget.onPageChanged,
controller: _pageController,
itemBuilder: (context, index) {
return Visibility(
visible: false,
maintainState: true,
maintainAnimation: true,
maintainInteractivity: true,
maintainSemantics: true,
maintainSize: true,
child: Stack(
alignment: widget.alignment,
children: [
GestureDetector(
onTap: () {
widget.onCardClick
?.call(index - widget.selectableCardId);
},
child: widget.builder
.call(context, index - widget.selectableCardId),
),
],
),
);
},
),
),
],
);
}
}
export 'package:carousel/src/carousel.dart';
export 'package:carousel/src/models/card_transform.dart';

134
lib/src/carousel.dart Normal file
View file

@ -0,0 +1,134 @@
import 'package:carousel/src/models/card_transform.dart';
import 'package:carousel/src/widgets/carousel_card.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
typedef CarouselCardBuilder = Widget Function(BuildContext context, int index);
class Carousel extends StatefulWidget {
/// Animated cards by swiping.
/// Each card can change its rotation, position and scale when swiping the cards.
/// Transform path can be privided using [transforms]
const Carousel({
required this.transforms,
required this.builder,
required this.selectableCardId,
this.pageViewHeight = 300,
this.onPageChanged,
this.alignment = AlignmentDirectional.topStart,
this.onCardClick,
Key? key,
}) : super(key: key);
/// A list of transforms to calculate the position of the card when swiping.
/// Every item in the list is one of the possible card positions.
final List<CardTransform> transforms;
/// The index of the transform card which acts as the selected card.
final int selectableCardId;
/// Builder for the card given a [context] and a [index] to identify the right card.
final CarouselCardBuilder builder;
/// Called when selected card is changed to the next one.
final void Function(int value)? onPageChanged;
// Callen when selected card is clicked.
final void Function(int value)? onCardClick;
/// Alignment of the cards.
final AlignmentGeometry alignment;
/// Size of the pageview used to capture swipe gestures.
final double pageViewHeight;
@override
State<Carousel> createState() => _CarouselState();
}
class _CarouselState extends State<Carousel> {
final PageController _pageController = PageController(initialPage: 0);
double _currentPage = 0;
@override
void initState() {
_pageController.addListener(() {
_currentPage = _pageController.page!;
});
super.initState();
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
alignment: widget.alignment,
children: [
AnimatedBuilder(
animation: _pageController,
builder: (context, _) {
final transitionPos = _currentPage % 1;
final index = _currentPage.floor();
final length = widget.transforms.length - 1;
return Stack(
children: [
for (var i = 0; i < length; i++) ...[
CarouselCard(
cardTransform: widget.transforms[i]
.transform(widget.transforms[i + 1], transitionPos),
child: widget.builder.call(context, index - i),
),
],
],
);
},
),
SizedBox(
height: widget.pageViewHeight,
child: PageView.builder(
scrollBehavior: _MouseSwipeOnWeb(),
onPageChanged: widget.onPageChanged,
controller: _pageController,
itemBuilder: (context, index) {
return Visibility(
visible: false,
maintainState: true,
maintainAnimation: true,
maintainInteractivity: true,
maintainSemantics: true,
maintainSize: true,
child: Stack(
alignment: widget.alignment,
children: [
GestureDetector(
onTap: () {
widget.onCardClick
?.call(index - widget.selectableCardId);
},
child: widget.builder
.call(context, index - widget.selectableCardId),
),
],
),
);
},
),
),
],
);
}
}
class _MouseSwipeOnWeb extends MaterialScrollBehavior {
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
};
}

View file

@ -1,4 +1,5 @@
class CardTransform {
/// Used by [Carousel] to build cards on the correct position.
CardTransform({
this.x = 0,
this.y = 0,
@ -10,6 +11,9 @@ class CardTransform {
double angle;
double scale;
/// [transitionPos] is a position value of a swipe for example.
/// [other] is the position, scale, rotation
/// which the current [CardTransform] need to be transformed to.
CardTransform transform(CardTransform other, double transitionPos) {
return CardTransform(
x: _transformValue(x, other.x, transitionPos),

View file

@ -1,6 +1,7 @@
import 'package:carousel/models/card_transform.dart';
import 'package:carousel/src/models/card_transform.dart';
import 'package:flutter/material.dart';
/// Transformed card used in [Carousel]
class CarouselCard extends StatelessWidget {
const CarouselCard({
required this.cardTransform,

View file

@ -1,6 +1,6 @@
import 'dart:math';
import 'package:carousel/models/card_transform.dart';
import 'package:carousel/src/models/card_transform.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {