Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to specify autoplay delay #420

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ CarouselSlider(
autoPlay: true,
autoPlayInterval: Duration(seconds: 3),
autoPlayAnimationDuration: Duration(milliseconds: 800),
autoPlayDelay: Duration(seconds: 1),
autoPlayCurve: Curves.fastOutSlowIn,
enlargeCenterPage: true,
enlargeFactor: 0.3,
Expand Down
10 changes: 9 additions & 1 deletion lib/carousel_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ class CarouselOptions {
/// Defaults to 800 ms.
final Duration autoPlayAnimationDuration;

/// How long until autoplay starts
///
/// Defaults to [autoPlayInterval].
final Duration autoPlayDelay;

/// Determines the animation curve physics.
///
/// Defaults to [Curves.fastOutSlowIn].
Expand Down Expand Up @@ -144,6 +149,7 @@ class CarouselOptions {
this.autoPlay: false,
this.autoPlayInterval: const Duration(seconds: 4),
this.autoPlayAnimationDuration = const Duration(milliseconds: 800),
autoPlayDelay,
this.autoPlayCurve: Curves.fastOutSlowIn,
this.enlargeCenterPage = false,
this.onPageChanged,
Expand All @@ -160,7 +166,7 @@ class CarouselOptions {
this.disableCenter: false,
this.padEnds = true,
this.clipBehavior: Clip.hardEdge,
});
}): this.autoPlayDelay = autoPlayDelay ?? autoPlayInterval;

///Generate new [CarouselOptions] based on old ones.

Expand All @@ -174,6 +180,7 @@ class CarouselOptions {
bool? autoPlay,
Duration? autoPlayInterval,
Duration? autoPlayAnimationDuration,
Duration? autoPlayDelay,
Curve? autoPlayCurve,
bool? enlargeCenterPage,
Function(int index, CarouselPageChangedReason reason)? onPageChanged,
Expand Down Expand Up @@ -201,6 +208,7 @@ class CarouselOptions {
autoPlayInterval: autoPlayInterval ?? this.autoPlayInterval,
autoPlayAnimationDuration:
autoPlayAnimationDuration ?? this.autoPlayAnimationDuration,
autoPlayDelay: autoPlayDelay ?? this.autoPlayDelay,
autoPlayCurve: autoPlayCurve ?? this.autoPlayCurve,
enlargeCenterPage: enlargeCenterPage ?? this.enlargeCenterPage,
onPageChanged: onPageChanged ?? this.onPageChanged,
Expand Down
155 changes: 85 additions & 70 deletions lib/carousel_slider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import 'utils.dart';
export 'carousel_controller.dart';
export 'carousel_options.dart';

typedef Widget ExtendedIndexedWidgetBuilder(
BuildContext context, int index, int realIndex);
typedef Widget ExtendedIndexedWidgetBuilder(BuildContext context, int index, int realIndex);

class CarouselSlider extends StatefulWidget {
/// [CarouselOptions] to create a [CarouselState] with
Expand All @@ -37,10 +36,10 @@ class CarouselSlider extends StatefulWidget {

CarouselSlider(
{required this.items,
required this.options,
this.disableGesture,
CarouselController? carouselController,
Key? key})
required this.options,
this.disableGesture,
CarouselController? carouselController,
Key? key})
: itemBuilder = null,
itemCount = items != null ? items.length : 0,
_carouselController = carouselController != null
Expand All @@ -51,11 +50,11 @@ class CarouselSlider extends StatefulWidget {
/// The on demand item builder constructor
CarouselSlider.builder(
{required this.itemCount,
required this.itemBuilder,
required this.options,
this.disableGesture,
CarouselController? carouselController,
Key? key})
required this.itemBuilder,
required this.options,
this.disableGesture,
CarouselController? carouselController,
Key? key})
: items = null,
_carouselController = carouselController != null
? carouselController as CarouselControllerImpl
Expand Down Expand Up @@ -126,42 +125,50 @@ class CarouselSliderState extends State<CarouselSlider>
carouselState!.pageController = pageController;
}

Timer? getTimer() {
return widget.options.autoPlay
? Timer.periodic(widget.options.autoPlayInterval, (_) {
if (!mounted) {
clearTimer();
return;
}
initializeTimer() {
if (widget.options.autoPlay) {
// Run the timer callback immediately before setting up the timer, this gives us better precision over when the
// operation starts since the Timer waits until the interval has passed before it starts it's periodic callbacks.
timerCallback();

final route = ModalRoute.of(context);
if (route?.isCurrent == false) {
return;
}
// Setup the timer to run periodically
timer = Timer.periodic(widget.options.autoPlayInterval, (_) => timerCallback());
}
}

void timerCallback() {
if (!mounted) {
clearTimer();
return;
}

CarouselPageChangedReason previousReason = mode;
changeMode(CarouselPageChangedReason.timed);
int nextPage = carouselState!.pageController!.page!.round() + 1;
int itemCount = widget.itemCount ?? widget.items!.length;
final route = ModalRoute.of(context);
if (route?.isCurrent == false) {
return;
}

if (nextPage >= itemCount &&
widget.options.enableInfiniteScroll == false) {
if (widget.options.pauseAutoPlayInFiniteScroll) {
clearTimer();
return;
}
nextPage = 0;
}
CarouselPageChangedReason previousReason = mode;
changeMode(CarouselPageChangedReason.timed);
int nextPage = carouselState!.pageController!.page!.round() + 1;
int itemCount = widget.itemCount ?? widget.items!.length;

carouselState!.pageController!
.animateToPage(nextPage,
duration: widget.options.autoPlayAnimationDuration,
curve: widget.options.autoPlayCurve)
.then((_) => changeMode(previousReason));
})
: null;
if (nextPage >= itemCount &&
widget.options.enableInfiniteScroll == false) {
if (widget.options.pauseAutoPlayInFiniteScroll) {
clearTimer();
return;
}
nextPage = 0;
}

carouselState!.pageController!
.animateToPage(nextPage,
duration: widget.options.autoPlayAnimationDuration,
curve: widget.options.autoPlayCurve)
.then((_) => changeMode(previousReason));
}


void clearTimer() {
if (timer != null) {
timer?.cancel();
Expand All @@ -171,19 +178,21 @@ class CarouselSliderState extends State<CarouselSlider>

void resumeTimer() {
if (timer == null) {
timer = getTimer();
initializeTimer();
}
}

void handleAutoPlay() {
bool autoPlayEnabled = widget.options.autoPlay;
Future.delayed(widget.options.autoPlayDelay, () {
bool autoPlayEnabled = widget.options.autoPlay;

if (autoPlayEnabled && timer != null) return;

clearTimer();
if (autoPlayEnabled) {
resumeTimer();
}
clearTimer();
if (autoPlayEnabled) {
resumeTimer();
}
});
}

Widget getGestureWrapper(Widget child) {
Expand Down Expand Up @@ -212,22 +221,22 @@ class CarouselSliderState extends State<CarouselSlider>
behavior: HitTestBehavior.opaque,
gestures: {
_MultipleGestureRecognizer:
GestureRecognizerFactoryWithHandlers<_MultipleGestureRecognizer>(
GestureRecognizerFactoryWithHandlers<_MultipleGestureRecognizer>(
() => _MultipleGestureRecognizer(),
(_MultipleGestureRecognizer instance) {
instance.onStart = (_) {
onStart();
};
instance.onDown = (_) {
onPanDown();
};
instance.onEnd = (_) {
onPanUp();
};
instance.onCancel = () {
onPanUp();
};
}),
instance.onStart = (_) {
onStart();
};
instance.onDown = (_) {
onPanDown();
};
instance.onEnd = (_) {
onPanUp();
};
instance.onCancel = () {
onPanUp();
};
}),
},
child: NotificationListener(
onNotification: (Notification notification) {
Expand All @@ -253,9 +262,9 @@ class CarouselSliderState extends State<CarouselSlider>

Widget getEnlargeWrapper(Widget? child,
{double? width,
double? height,
double? scale,
required double itemOffset}) {
double? height,
double? scale,
required double itemOffset}) {
if (widget.options.enlargeStrategy == CenterPageEnlargeStrategy.height) {
return SizedBox(child: child, width: width, height: height);
}
Expand Down Expand Up @@ -355,8 +364,8 @@ class CarouselSliderState extends State<CarouselSlider>
BuildContext storageContext = carouselState!
.pageController!.position.context.storageContext;
final double? previousSavedPosition =
PageStorage.of(storageContext)?.readState(storageContext)
as double?;
PageStorage.of(storageContext)?.readState(storageContext)
as double?;
if (previousSavedPosition != null) {
itemOffset = previousSavedPosition - idx.toDouble();
} else {
Expand All @@ -366,15 +375,18 @@ class CarouselSliderState extends State<CarouselSlider>
}

final double enlargeFactor =
options.enlargeFactor.clamp(0.0, 1.0);
options.enlargeFactor.clamp(0.0, 1.0);
final num distortionRatio =
(1 - (itemOffset.abs() * enlargeFactor)).clamp(0.0, 1.0);
(1 - (itemOffset.abs() * enlargeFactor)).clamp(0.0, 1.0);
distortionValue =
Curves.easeOut.transform(distortionRatio as double);
}

final double height = widget.options.height ??
MediaQuery.of(context).size.width *
MediaQuery
.of(context)
.size
.width *
(1 / widget.options.aspectRatio);

if (widget.options.scrollDirection == Axis.horizontal) {
Expand All @@ -384,7 +396,10 @@ class CarouselSliderState extends State<CarouselSlider>
itemOffset: itemOffset));
} else {
return getCenterWrapper(getEnlargeWrapper(child,
width: distortionValue * MediaQuery.of(context).size.width,
width: distortionValue * MediaQuery
.of(context)
.size
.width,
scale: distortionValue,
itemOffset: itemOffset));
}
Expand Down