diff --git a/lib/src/infinite_carousel.dart b/lib/src/infinite_carousel.dart index c8e683d..dfd2020 100644 --- a/lib/src/infinite_carousel.dart +++ b/lib/src/infinite_carousel.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:math' as math; import 'package:flutter/physics.dart'; @@ -22,6 +23,8 @@ class InfiniteCarousel extends StatefulWidget { required this.itemBuilder, this.physics, this.controller, + this.autoScroll = false, + this.speed = 1, this.onIndexChanged, this.anchor = 0.0, this.loop = true, @@ -44,6 +47,12 @@ class InfiniteCarousel extends StatefulWidget { ) : null; + /// auto scroll + final bool autoScroll; + + /// 16 ms scroll distance + final double speed; + /// Total items to build for the carousel. final int itemCount; @@ -117,6 +126,7 @@ class _InfiniteCarouselState extends State { final Key _forwardListKey = const ValueKey('infinite_carousel_key'); late InfiniteScrollController scrollController; late int _lastReportedItemIndex; + Timer? _timer; @override void initState() { @@ -128,6 +138,7 @@ class _InfiniteCarouselState extends State { scrollController = InfiniteScrollController(); } _lastReportedItemIndex = scrollController.initialItem; + _resetTimer(); } List _buildSlivers() { @@ -156,6 +167,28 @@ class _InfiniteCarouselState extends State { } } + void _resetTimer() { + if (!widget.autoScroll) { + return; + } + _clearTimer(); + _timer = Timer.periodic( + const Duration(milliseconds: 16), + (timer) { + if (scrollController.positions.isNotEmpty) { + scrollController.jumpTo( + scrollController.position.pixels + widget.speed, + ); + } + }, + ); + } + + void _clearTimer() { + _timer?.cancel(); + _timer = null; + } + @override Widget build(BuildContext context) { final AxisDirection axisDirection = _getDirection(context); @@ -179,26 +212,36 @@ class _InfiniteCarouselState extends State { child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { final centeredAnchor = _getCenteredAnchor(constraints); - - return _InfiniteScrollable( - controller: scrollController, - itemExtent: widget.itemExtent, - loop: widget.loop, - velocityFactor: widget.velocityFactor, - itemCount: widget.itemCount, - physics: widget.physics ?? const InfiniteScrollPhysics(), - axisDirection: axisDirection, - scrollBehavior: widget.scrollBehavior ?? - ScrollConfiguration.of(context).copyWith(scrollbars: false), - viewportBuilder: (BuildContext context, ViewportOffset position) { - return Viewport( - center: _forwardListKey, - anchor: centeredAnchor, - axisDirection: axisDirection, - offset: position, - slivers: _buildSlivers(), - ); + return Listener( + onPointerDown: (event) { + _clearTimer(); + }, + onPointerUp: (event) { + _resetTimer(); }, + onPointerCancel: (event) { + _resetTimer(); + }, + child: _InfiniteScrollable( + controller: scrollController, + itemExtent: widget.itemExtent, + loop: widget.loop, + velocityFactor: widget.velocityFactor, + itemCount: widget.itemCount, + physics: widget.physics ?? const InfiniteScrollPhysics(), + axisDirection: axisDirection, + scrollBehavior: widget.scrollBehavior ?? + ScrollConfiguration.of(context).copyWith(scrollbars: false), + viewportBuilder: (BuildContext context, ViewportOffset position) { + return Viewport( + center: _forwardListKey, + anchor: centeredAnchor, + axisDirection: axisDirection, + offset: position, + slivers: _buildSlivers(), + ); + }, + ), ); }, ), @@ -214,6 +257,12 @@ class _InfiniteCarouselState extends State { : constraints.maxHeight; return ((total / 2) - (widget.itemExtent / 2)) / total; } + + @override + void dispose() { + _clearTimer(); + super.dispose(); + } } /// Extend Scrollable to also include viewport children's itemExtent, itemCount, loop and other values.