diff --git a/example/pubspec.lock b/example/pubspec.lock index 2b5364b..8d69009 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.2" + version: "2.9.0" boolean_selector: dependency: transitive description: @@ -28,21 +28,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" + version: "1.2.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: @@ -63,7 +56,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -101,28 +94,28 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.11" + version: "0.12.12" material_color_utilities: dependency: transitive description: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" sky_engine: dependency: transitive description: flutter @@ -134,7 +127,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -155,21 +148,21 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.12" vector_math: dependency: transitive description: diff --git a/lib/src/custom_layout.dart b/lib/src/custom_layout.dart index 2b0e0ec..f056790 100644 --- a/lib/src/custom_layout.dart +++ b/lib/src/custom_layout.dart @@ -10,10 +10,12 @@ abstract class _CustomLayoutStateBase extends State late int _startIndex; int? _animationCount; int _currentIndex = 0; + late bool _reversePan; @override void initState() { _currentIndex = widget.index ?? 0; + _reversePan = widget.reversePan; if (widget.itemWidth == null) { throw Exception( '==============\n\nwidget.itemWidth must not be null when use stack layout.\n========\n', @@ -214,12 +216,20 @@ abstract class _CustomLayoutStateBase extends State if (_currentIndex <= 0 && !widget.loop) { return; } - _move(1.0, nextIndex: _currentIndex - 1); + if (_reversePan) { + _move(0.0, nextIndex: _currentIndex + 1); + } else { + _move(1.0, nextIndex: _currentIndex - 1); + } } else if (_animationController.value < 0.25 || velocity < -500.0) { if (_currentIndex >= widget.itemCount - 1 && !widget.loop) { return; } - _move(0.0, nextIndex: _currentIndex + 1); + if (_reversePan) { + _move(1.0, nextIndex: _currentIndex - 1); + } else { + _move(0.0, nextIndex: _currentIndex + 1); + } } else { _move(0.5); } @@ -235,13 +245,24 @@ abstract class _CustomLayoutStateBase extends State void _onPanUpdate(DragUpdateDetails details) { if (_lockScroll) return; - var value = _currentValue + - ((widget.scrollDirection == Axis.horizontal - ? details.globalPosition.dx - : details.globalPosition.dy) - - _currentPos) / - _swiperWidth / - 2; + late double value; + if (_reversePan) { + value = _currentValue - + ((widget.scrollDirection == Axis.horizontal + ? details.globalPosition.dx + : details.globalPosition.dy) - + _currentPos) / + _swiperWidth / + 2; + } else { + value = _currentValue + + ((widget.scrollDirection == Axis.horizontal + ? details.globalPosition.dx + : details.globalPosition.dy) - + _currentPos) / + _swiperWidth / + 2; + } // no loop ? if (!widget.loop) { if (_currentIndex >= widget.itemCount - 1) { diff --git a/lib/src/swiper.dart b/lib/src/swiper.dart index e0e3918..6a8e483 100644 --- a/lib/src/swiper.dart +++ b/lib/src/swiper.dart @@ -30,6 +30,7 @@ const int kMiddleValue = 1000000000; enum SwiperLayout { DEFAULT, STACK, + STACK_REVERSE, TINDER, CUSTOM, } @@ -498,6 +499,21 @@ class _SwiperState extends _SwiperTimerMixin { scrollDirection: widget.scrollDirection, axisDirection: widget.axisDirection, ); + } else if (widget.layout == SwiperLayout.STACK_REVERSE) { + return _StackReverseSwiper( + loop: widget.loop, + itemWidth: widget.itemWidth, + itemHeight: widget.itemHeight, + itemCount: widget.itemCount, + itemBuilder: itemBuilder, + index: _activeIndex, + curve: widget.curve, + duration: widget.duration, + onIndexChanged: _onIndexChanged, + controller: _controller, + scrollDirection: widget.scrollDirection, + axisDirection: widget.axisDirection, + ); } else if (_isPageViewLayout()) { //default var transformer = widget.transformer; @@ -696,22 +712,24 @@ abstract class _SubSwiper extends StatefulWidget { final bool loop; final Axis? scrollDirection; final AxisDirection? axisDirection; - - const _SubSwiper({ - Key? key, - required this.loop, - this.itemHeight, - this.itemWidth, - this.duration, - required this.curve, - this.itemBuilder, - required this.controller, - this.index, - required this.itemCount, - this.scrollDirection = Axis.horizontal, - this.axisDirection = AxisDirection.left, - this.onIndexChanged, - }) : super(key: key); + final bool reversePan; + + const _SubSwiper( + {Key? key, + required this.loop, + this.itemHeight, + this.itemWidth, + this.duration, + required this.curve, + this.itemBuilder, + required this.controller, + this.index, + required this.itemCount, + this.scrollDirection = Axis.horizontal, + this.axisDirection = AxisDirection.left, + this.onIndexChanged, + this.reversePan = false}) + : super(key: key); @override State createState(); @@ -796,6 +814,41 @@ class _StackSwiper extends _SubSwiper { State createState() => _StackViewState(); } +class _StackReverseSwiper extends _SubSwiper { + const _StackReverseSwiper({ + Key? key, + required Curve curve, + int? duration, + required SwiperController controller, + ValueChanged? onIndexChanged, + double? itemHeight, + double? itemWidth, + IndexedWidgetBuilder? itemBuilder, + int? index, + required bool loop, + required int itemCount, + Axis? scrollDirection, + AxisDirection? axisDirection, + }) : super( + loop: loop, + key: key, + itemWidth: itemWidth, + itemHeight: itemHeight, + itemBuilder: itemBuilder, + curve: curve, + duration: duration, + controller: controller, + index: index, + onIndexChanged: onIndexChanged, + itemCount: itemCount, + scrollDirection: scrollDirection, + axisDirection: axisDirection, + reversePan: true); + + @override + State createState() => _StackReverseViewState(); +} + class _TinderState extends _CustomLayoutStateBase<_TinderSwiper> { late List scales; late List offsetsX; @@ -972,6 +1025,89 @@ class _StackViewState extends _CustomLayoutStateBase<_StackSwiper> { } } +class _StackReverseViewState extends _CustomLayoutStateBase<_StackSwiper> { + late List scales; + late List offsets; + late List opacity; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + } + + void _updateValues() { + if (widget.scrollDirection == Axis.horizontal) { + final space = (_swiperWidth - widget.itemWidth!) / 2; + offsets = widget.axisDirection == AxisDirection.left + ? [-space, -space / 3 * 2, -space / 3, 0.0, _swiperWidth] + : [_swiperWidth, 0.0, -space / 3, -space / 3 * 2, -space]; + } else { + final space = (_swiperHeight - widget.itemHeight!) / 2; + offsets = [-space, -space / 3 * 2, -space / 2, 0.0, _swiperHeight]; + } + } + + @override + void didUpdateWidget(_StackSwiper oldWidget) { + _updateValues(); + super.didUpdateWidget(oldWidget); + } + + @override + void afterRender() { + super.afterRender(); + final isRightSide = widget.axisDirection == AxisDirection.right; + + //length of the values array below + _animationCount = 5; + + //Array below this line, '0' index is 1.0, which is the first item show in swiper. + _startIndex = isRightSide ? -1 : -2; + scales = + isRightSide ? [1.0, 1.0, 0.9, 0.8, 0.7] : [0.7, 0.8, 0.9, 1.0, 1.0]; + opacity = + isRightSide ? [1.0, 1.0, 1.0, 0.5, 0.0] : [0.0, 0.5, 1.0, 1.0, 1.0]; + + _updateValues(); + } + + @override + Widget _buildItem(int i, int realIndex, double animationValue) { + final s = _getValue(scales, animationValue, i); + final f = _getValue(offsets, animationValue, i); + final o = _getValue(opacity, animationValue, i); + + final offset = widget.scrollDirection == Axis.horizontal + ? widget.axisDirection == AxisDirection.left + ? Offset(f, 0.0) + : Offset(-f, 0.0) + : Offset(0.0, -f); + + final alignment = widget.scrollDirection == Axis.horizontal + ? widget.axisDirection == AxisDirection.left + ? Alignment.centerLeft + : Alignment.centerRight + : Alignment.topCenter; + + return Opacity( + opacity: o, + child: Transform.translate( + key: ValueKey(_currentIndex + i), + offset: offset, + child: Transform.scale( + scale: s, + alignment: alignment, + child: SizedBox( + width: widget.itemWidth ?? double.infinity, + height: widget.itemHeight ?? double.infinity, + child: widget.itemBuilder!(context, realIndex), + ), + ), + ), + ); + } +} + class ScaleAndFadeTransformer extends PageTransformer { final double? _scale; final double? _fade; diff --git a/pubspec.lock b/pubspec.lock index c9155c0..5e0abf2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.2" + version: "2.9.0" boolean_selector: dependency: transitive description: @@ -21,21 +21,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" + version: "1.2.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: @@ -49,7 +42,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -87,28 +80,28 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.11" + version: "0.12.12" material_color_utilities: dependency: transitive description: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" sky_engine: dependency: transitive description: flutter @@ -120,7 +113,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -141,21 +134,21 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.12" vector_math: dependency: transitive description: