mirror of
https://github.com/Iconica-Development/flutter_carousel.git
synced 2025-05-19 12:13:46 +02:00
initial commit
This commit is contained in:
parent
e260f49c1e
commit
3fa92aa849
8 changed files with 171 additions and 128 deletions
29
README.md
29
README.md
|
@ -1,2 +1,27 @@
|
||||||
# carousel
|
# Carousel
|
||||||
card carousel widget
|
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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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
BIN
demo.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.3 MiB |
|
@ -1,5 +1,5 @@
|
||||||
import 'package:carousel/carousel.dart';
|
import 'package:carousel/carousel.dart';
|
||||||
import 'package:carousel/models/card_transform.dart';
|
|
||||||
import 'package:carousel_example/pokemon.dart';
|
import 'package:carousel_example/pokemon.dart';
|
||||||
import 'package:carousel_example/pokemon_card.dart';
|
import 'package:carousel_example/pokemon_card.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
|
@ -1,125 +1,4 @@
|
||||||
library carousel;
|
library carousel;
|
||||||
|
|
||||||
export 'package:carousel/carousel.dart';
|
export 'package:carousel/src/carousel.dart';
|
||||||
|
export 'package:carousel/src/models/card_transform.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),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
134
lib/src/carousel.dart
Normal file
134
lib/src/carousel.dart
Normal 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,
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
class CardTransform {
|
class CardTransform {
|
||||||
|
/// Used by [Carousel] to build cards on the correct position.
|
||||||
CardTransform({
|
CardTransform({
|
||||||
this.x = 0,
|
this.x = 0,
|
||||||
this.y = 0,
|
this.y = 0,
|
||||||
|
@ -10,6 +11,9 @@ class CardTransform {
|
||||||
double angle;
|
double angle;
|
||||||
double scale;
|
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) {
|
CardTransform transform(CardTransform other, double transitionPos) {
|
||||||
return CardTransform(
|
return CardTransform(
|
||||||
x: _transformValue(x, other.x, transitionPos),
|
x: _transformValue(x, other.x, transitionPos),
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:carousel/models/card_transform.dart';
|
import 'package:carousel/src/models/card_transform.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// Transformed card used in [Carousel]
|
||||||
class CarouselCard extends StatelessWidget {
|
class CarouselCard extends StatelessWidget {
|
||||||
const CarouselCard({
|
const CarouselCard({
|
||||||
required this.cardTransform,
|
required this.cardTransform,
|
|
@ -1,6 +1,6 @@
|
||||||
import 'dart:math';
|
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';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|
Loading…
Reference in a new issue