diff --git a/CHANGELOG.md b/CHANGELOG.md index 559a6bb..73faede 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 5.1.1 + +- [Fix] Fix lint issue + +# 5.1.0 + +- [Feat] Support Apple Pencil +- [Fix] Fix RangeError (length): Invalid value: Valid value range is empty: 0 + +# 5.0.0 + +- [FIX] Rename CarouselController to CarouselSliderController + # 4.2.1 - [FIX] temporary remove `PointerDeviceKind.trackpad` diff --git a/README.md b/README.md index b0822d5..c23cc96 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Note: this page is built with flutter-web. For a better user experience, please ## Installation -Add `carousel_slider: ^4.2.1` to your `pubspec.yaml` dependencies. And import it: +Add `carousel_slider: ^5.1.1` to your `pubspec.yaml` dependencies. And import it: ```dart import 'package:carousel_slider/carousel_slider.dart'; @@ -100,11 +100,11 @@ CarouselSlider.builder( ## Carousel controller -In order to manually control the pageview's position, you can create your own `CarouselController`, and pass it to `CarouselSlider`. Then you can use the `CarouselController` instance to manipulate the position. +In order to manually control the pageview's position, you can create your own `CarouselSliderController`, and pass it to `CarouselSlider`. Then you can use the `CarouselSliderController` instance to manipulate the position. ```dart class CarouselDemo extends StatelessWidget { - CarouselController buttonCarouselController = CarouselController(); + CarouselSliderController buttonCarouselController = CarouselSliderController(); @override Widget build(BuildContext context) => Column( @@ -130,7 +130,7 @@ class CarouselDemo extends StatelessWidget { } ``` -### `CarouselController` methods +### `CarouselSliderController` methods #### `.nextPage({Duration duration, Curve curve})` @@ -170,7 +170,7 @@ Image carousel slider with custom indicator demo: ![indicator](screenshot/indicator.gif) -Custom `CarouselController` and manually control the pageview position demo: +Custom `CarouselSliderController` and manually control the pageview position demo: ![manual](screenshot/manually.gif) diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 4f8d4d2..8c6e561 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 3ef0cbd..bc07d89 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -127,7 +127,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -171,10 +171,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -185,6 +187,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -272,7 +275,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -358,7 +361,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -407,7 +410,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3db53b6..e67b280 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ CADisableMinimumFrameDurationOnPhone + UIApplicationSupportsIndirectInputEvents + diff --git a/example/lib/main.dart b/example/lib/main.dart index 59129fe..3e860d6 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -255,7 +255,7 @@ class ManuallyControlledSlider extends StatefulWidget { } class _ManuallyControlledSliderState extends State { - final CarouselController _controller = CarouselController(); + final CarouselSliderController _controller = CarouselSliderController(); @override void initState() { @@ -408,7 +408,7 @@ class CarouselWithIndicatorDemo extends StatefulWidget { class _CarouselWithIndicatorState extends State { int _current = 0; - final CarouselController _controller = CarouselController(); + final CarouselSliderController _controller = CarouselSliderController(); @override Widget build(BuildContext context) { @@ -515,7 +515,7 @@ class CarouselChangeReasonDemo extends StatefulWidget { class _CarouselChangeReasonDemoState extends State { String reason = ''; - final CarouselController _controller = CarouselController(); + final CarouselSliderController _controller = CarouselSliderController(); void onPageChange(int index, CarouselPageChangedReason changeReason) { setState(() { diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index cc89c87..d9ae3f4 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -182,7 +182,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -235,6 +235,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -344,7 +345,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -423,7 +424,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -470,7 +471,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index ae8ff59..ec9aa6b 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Bool { return true diff --git a/lib/carousel_controller.dart b/lib/carousel_controller.dart index 501b2c0..280f996 100644 --- a/lib/carousel_controller.dart +++ b/lib/carousel_controller.dart @@ -6,7 +6,7 @@ import 'carousel_options.dart'; import 'carousel_state.dart'; import 'utils.dart'; -abstract class CarouselController { +abstract class CarouselSliderController { bool get ready; Future get onReady; @@ -23,10 +23,10 @@ abstract class CarouselController { void stopAutoPlay(); - factory CarouselController() => CarouselControllerImpl(); + factory CarouselSliderController() => CarouselSliderControllerImpl(); } -class CarouselControllerImpl implements CarouselController { +class CarouselSliderControllerImpl implements CarouselSliderController { final Completer _readyCompleter = Completer(); CarouselState? _state; diff --git a/lib/carousel_options.dart b/lib/carousel_options.dart index ec29cd3..c8f5014 100644 --- a/lib/carousel_options.dart +++ b/lib/carousel_options.dart @@ -203,7 +203,8 @@ class CarouselOptions { reverse: reverse ?? this.reverse, autoPlay: autoPlay ?? this.autoPlay, autoPlayInterval: autoPlayInterval ?? this.autoPlayInterval, - autoPlayAnimationDuration: autoPlayAnimationDuration ?? this.autoPlayAnimationDuration, + autoPlayAnimationDuration: + autoPlayAnimationDuration ?? this.autoPlayAnimationDuration, autoPlayCurve: autoPlayCurve ?? this.autoPlayCurve, enlargeCenterPage: enlargeCenterPage ?? this.enlargeCenterPage, onPageChanged: onPageChanged ?? this.onPageChanged, @@ -212,8 +213,10 @@ class CarouselOptions { pageSnapping: pageSnapping ?? this.pageSnapping, scrollDirection: scrollDirection ?? this.scrollDirection, pauseAutoPlayOnTouch: pauseAutoPlayOnTouch ?? this.pauseAutoPlayOnTouch, - pauseAutoPlayOnManualNavigate: pauseAutoPlayOnManualNavigate ?? this.pauseAutoPlayOnManualNavigate, - pauseAutoPlayInFiniteScroll: pauseAutoPlayInFiniteScroll ?? this.pauseAutoPlayInFiniteScroll, + pauseAutoPlayOnManualNavigate: + pauseAutoPlayOnManualNavigate ?? this.pauseAutoPlayOnManualNavigate, + pauseAutoPlayInFiniteScroll: + pauseAutoPlayInFiniteScroll ?? this.pauseAutoPlayInFiniteScroll, pageViewKey: pageViewKey ?? this.pageViewKey, enlargeStrategy: enlargeStrategy ?? this.enlargeStrategy, enlargeFactor: enlargeFactor ?? this.enlargeFactor, diff --git a/lib/carousel_slider.dart b/lib/carousel_slider.dart index 9910c8b..37865c5 100644 --- a/lib/carousel_slider.dart +++ b/lib/carousel_slider.dart @@ -13,7 +13,8 @@ import 'utils.dart'; export 'carousel_controller.dart'; export 'carousel_options.dart'; -typedef Widget ExtendedIndexedWidgetBuilder(BuildContext context, int index, int realIndex); +typedef ExtendedIndexedWidgetBuilder = Widget Function( + BuildContext context, int index, int realIndex); class CarouselSlider extends StatefulWidget { /// [CarouselOptions] to create a [CarouselState] with @@ -30,7 +31,7 @@ class CarouselSlider extends StatefulWidget { final ExtendedIndexedWidgetBuilder? itemBuilder; /// A [MapController], used to control the map. - final CarouselControllerImpl _carouselController; + final CarouselSliderControllerImpl _carouselController; final int? itemCount; @@ -38,13 +39,13 @@ class CarouselSlider extends StatefulWidget { {required this.items, required this.options, this.disableGesture, - CarouselController? carouselController, + CarouselSliderController? carouselController, Key? key}) : itemBuilder = null, itemCount = items != null ? items.length : 0, _carouselController = carouselController != null - ? carouselController as CarouselControllerImpl - : CarouselController() as CarouselControllerImpl, + ? carouselController as CarouselSliderControllerImpl + : CarouselSliderController() as CarouselSliderControllerImpl, super(key: key); /// The on demand item builder constructor @@ -53,20 +54,21 @@ class CarouselSlider extends StatefulWidget { required this.itemBuilder, required this.options, this.disableGesture, - CarouselController? carouselController, + CarouselSliderController? carouselController, Key? key}) : items = null, _carouselController = carouselController != null - ? carouselController as CarouselControllerImpl - : CarouselController() as CarouselControllerImpl, + ? carouselController as CarouselSliderControllerImpl + : CarouselSliderController() as CarouselSliderControllerImpl, super(key: key); @override CarouselSliderState createState() => CarouselSliderState(_carouselController); } -class CarouselSliderState extends State with TickerProviderStateMixin { - final CarouselControllerImpl carouselController; +class CarouselSliderState extends State + with TickerProviderStateMixin { + final CarouselSliderControllerImpl carouselController; Timer? timer; CarouselOptions get options => widget.options; @@ -105,7 +107,8 @@ class CarouselSliderState extends State with TickerProviderState @override void initState() { super.initState(); - carouselState = CarouselState(this.options, clearTimer, resumeTimer, this.changeMode); + carouselState = + CarouselState(this.options, clearTimer, resumeTimer, this.changeMode); carouselState!.itemCount = widget.itemCount; carouselController.state = carouselState; @@ -141,7 +144,8 @@ class CarouselSliderState extends State with TickerProviderState int nextPage = carouselState!.pageController!.page!.round() + 1; int itemCount = widget.itemCount ?? widget.items!.length; - if (nextPage >= itemCount && widget.options.enableInfiniteScroll == false) { + if (nextPage >= itemCount && + widget.options.enableInfiniteScroll == false) { if (widget.options.pauseAutoPlayInFiniteScroll) { clearTimer(); return; @@ -151,7 +155,8 @@ class CarouselSliderState extends State with TickerProviderState carouselState!.pageController! .animateToPage(nextPage, - duration: widget.options.autoPlayAnimationDuration, curve: widget.options.autoPlayCurve) + duration: widget.options.autoPlayAnimationDuration, + curve: widget.options.autoPlayCurve) .then((_) => changeMode(previousReason)); }) : null; @@ -186,13 +191,15 @@ class CarouselSliderState extends State with TickerProviderState if (widget.options.height != null) { wrapper = Container(height: widget.options.height, child: child); } else { - wrapper = AspectRatio(aspectRatio: widget.options.aspectRatio, child: child); + wrapper = + AspectRatio(aspectRatio: widget.options.aspectRatio, child: child); } if (true == widget.disableGesture) { return NotificationListener( onNotification: (Notification notification) { - if (widget.options.onScrolled != null && notification is ScrollUpdateNotification) { + if (widget.options.onScrolled != null && + notification is ScrollUpdateNotification) { widget.options.onScrolled!(carouselState!.pageController!.page); } return false; @@ -204,8 +211,10 @@ class CarouselSliderState extends State with TickerProviderState return RawGestureDetector( behavior: HitTestBehavior.opaque, gestures: { - _MultipleGestureRecognizer: GestureRecognizerFactoryWithHandlers<_MultipleGestureRecognizer>( - () => _MultipleGestureRecognizer(), (_MultipleGestureRecognizer instance) { + _MultipleGestureRecognizer: + GestureRecognizerFactoryWithHandlers<_MultipleGestureRecognizer>( + () => _MultipleGestureRecognizer(), + (_MultipleGestureRecognizer instance) { instance.onStart = (_) { onStart(); }; @@ -222,7 +231,8 @@ class CarouselSliderState extends State with TickerProviderState }, child: NotificationListener( onNotification: (Notification notification) { - if (widget.options.onScrolled != null && notification is ScrollUpdateNotification) { + if (widget.options.onScrolled != null && + notification is ScrollUpdateNotification) { widget.options.onScrolled!(carouselState!.pageController!.page); } return false; @@ -241,7 +251,11 @@ class CarouselSliderState extends State with TickerProviderState return Center(child: child); } - Widget getEnlargeWrapper(Widget? child, {double? width, double? height, double? scale, required double itemOffset}) { + Widget getEnlargeWrapper(Widget? child, + {double? width, + double? height, + double? scale, + required double itemOffset}) { if (widget.options.enlargeStrategy == CenterPageEnlargeStrategy.height) { return SizedBox(child: child, width: width, height: height); } @@ -255,7 +269,9 @@ class CarouselSliderState extends State with TickerProviderState } return Transform.scale(child: child, scale: scale!, alignment: alignment); } - return Transform.scale(scale: scale!, child: Container(child: child, width: width, height: height)); + return Transform.scale( + scale: scale!, + child: Container(child: child, width: width, height: height)); } Widget getOverlayWrapper(Widget? child, double overlayOpacity) { @@ -309,6 +325,7 @@ class CarouselSliderState extends State with TickerProviderState dragDevices: { PointerDeviceKind.touch, PointerDeviceKind.mouse, + PointerDeviceKind.stylus, }, ), clipBehavior: widget.options.clipBehavior, @@ -320,59 +337,83 @@ class CarouselSliderState extends State with TickerProviderState itemCount: widget.options.enableInfiniteScroll ? null : widget.itemCount, key: widget.options.pageViewKey, onPageChanged: (int index) { - int currentPage = getRealIndex(index + carouselState!.initialPage, carouselState!.realPage, widget.itemCount); + int currentPage = getRealIndex(index + carouselState!.initialPage, + carouselState!.realPage, widget.itemCount); if (widget.options.onPageChanged != null) { widget.options.onPageChanged!(currentPage, mode); } }, itemBuilder: (BuildContext context, int idx) { - final int index = getRealIndex(idx + carouselState!.initialPage, carouselState!.realPage, widget.itemCount); + final int index = getRealIndex(idx + carouselState!.initialPage, + carouselState!.realPage, widget.itemCount); return AnimatedBuilder( animation: carouselState!.pageController!, child: (widget.items != null) - ? (widget.items!.length > 0 ? widget.items![index] : Container()) - : widget.itemBuilder!(context, index, idx), + ? (widget.items!.isNotEmpty ? widget.items![index] : Container()) + : ((widget.itemCount ?? 0) > 0 + ? widget.itemBuilder!(context, index, idx) + : const SizedBox.shrink()), builder: (BuildContext context, child) { double distortionValue = 1.0; // if `enlargeCenterPage` is true, we must calculate the carousel item's height // to display the visual effect double itemOffset = 0; double opacityValue = 0; - if (widget.options.enlargeCenterPage != null && widget.options.enlargeCenterPage == true) { + if (widget.options.enlargeCenterPage != null && + widget.options.enlargeCenterPage == true) { // pageController.page can only be accessed after the first build, // so in the first build we calculate the itemoffset manually var position = carouselState?.pageController?.position; - if (position != null && position.hasPixels && position.hasContentDimensions) { + if (position != null && + position.hasPixels && + position.hasContentDimensions) { var _page = carouselState?.pageController?.page; if (_page != null) { itemOffset = _page - idx; } } else { - BuildContext storageContext = carouselState!.pageController!.position.context.storageContext; + 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 { - itemOffset = carouselState!.realPage.toDouble() - idx.toDouble(); + itemOffset = + carouselState!.realPage.toDouble() - idx.toDouble(); } } - final double enlargeFactor = options.enlargeFactor.clamp(0.0, 1.0); - final num distortionRatio = (1 - (itemOffset.abs() * enlargeFactor)).clamp(0.0, 1.0); - distortionValue = Curves.easeOut.transform(distortionRatio as double); + // Check if the text direction is right-to-left (RTL) + if (Directionality.of(context) == TextDirection.rtl) { + // If RTL, invert the item offset + itemOffset = -itemOffset; + } + + final double enlargeFactor = + options.enlargeFactor.clamp(0.0, 1.0); + final num distortionRatio = + (1 - (itemOffset.abs() * enlargeFactor)).clamp(0.0, 1.0); + distortionValue = + Curves.easeOut.transform(distortionRatio as double); opacityValue = 1 - distortionRatio; } - final double height = - widget.options.height ?? MediaQuery.of(context).size.width * (1 / widget.options.aspectRatio); + final double height = widget.options.height ?? + MediaQuery.of(context).size.width * + (1 / widget.options.aspectRatio); if (widget.options.scrollDirection == Axis.horizontal) { - return getCenterWrapper(getEnlargeWrapper(getOverlayWrapper(child, opacityValue), - height: distortionValue * height, scale: distortionValue, itemOffset: itemOffset)); + return getCenterWrapper(getEnlargeWrapper( + getOverlayWrapper(child, opacityValue), + height: distortionValue * height, + scale: distortionValue, + itemOffset: itemOffset)); } else { - return getCenterWrapper(getEnlargeWrapper(getOverlayWrapper(child, opacityValue), + return getCenterWrapper(getEnlargeWrapper( + getOverlayWrapper(child, opacityValue), width: distortionValue * MediaQuery.of(context).size.width, scale: distortionValue, itemOffset: itemOffset)); diff --git a/pubspec.yaml b/pubspec.yaml index d4e9fef..a9b16ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,10 +2,10 @@ name: carousel_slider description: A carousel slider widget, support infinite scroll and custom child widget. homepage: https://github.com/serenader2014/flutter_carousel_slider repository: https://github.com/serenader2014/flutter_carousel_slider -version: 4.2.1 +version: 5.1.1 environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.12.0 <4.0.0' dependencies: flutter: