diff --git a/.travis.yml b/.travis.yml deleted file mode 100755 index 3b5aa4f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: dart -env: - - SHARD=dartfmt - - SHARD=test -install: - - ./dev/bots/travis_install.sh -script: - - ./dev/bots/travis_script.sh -after_success: - - coveralls-lcov coverage/lcov.info \ No newline at end of file diff --git a/LICENSE b/LICENSE index ba75c69..4770040 100644 --- a/LICENSE +++ b/LICENSE @@ -1 +1,21 @@ -TODO: Add your license here. +MIT License + +Copyright (c) 2018 Xueliang Ren + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/lib/another_transformer_page_view.dart b/lib/another_transformer_page_view.dart index 727ddb0..b102526 100644 --- a/lib/another_transformer_page_view.dart +++ b/lib/another_transformer_page_view.dart @@ -1,623 +1,3 @@ -library another_transformer_page_view; - -import 'package:flutter/widgets.dart'; -import 'package:another_transformer_page_view/index_controller.dart'; -export 'package:another_transformer_page_view/index_controller.dart'; -export 'package:another_transformer_page_view/parallax.dart'; - -/// -/// NOTICE:: -/// -/// In order to make package smaller,currently we're not supporting any build-in page transformers -/// You can find build in transforms here: -/// -/// -/// - -const int kMaxValue = 2000000000; -const int kMiddleValue = 1000000000; - -/// Default auto play transition duration (in millisecond) -const int kDefaultTransactionDuration = 300; - -class TransformInfo { - /// The `width` of the `TransformerPageView` - final double width; - - /// The `height` of the `TransformerPageView` - final double height; - - /// The `position` of the widget pass to [PageTransformer.transform] - /// A `position` describes how visible the widget is. - /// The widget in the center of the screen' which is full visible, position is 0.0. - /// The widge in the left ,may be hidden, of the screen's position is less than 0.0, -1.0 when out of the screen. - /// The widge in the right ,may be hidden, of the screen's position is greater than 0.0, 1.0 when out of the screen - /// - /// - final double position; - - /// The `index` of the widget pass to [PageTransformer.transform] - final int index; - - /// The `activeIndex` of the PageView - final int activeIndex; - - /// The `activeIndex` of the PageView, from user start to swipe - /// It will change when user end drag - final int fromIndex; - - /// Next `index` is greater than this `index` - final bool forward; - - /// User drag is done. - final bool done; - - /// Same as [TransformerPageView.viewportFraction] - final double viewportFraction; - - /// Copy from [TransformerPageView.scrollDirection] - final Axis scrollDirection; - - TransformInfo( - {this.index, - this.position, - this.width, - this.height, - this.activeIndex, - this.fromIndex, - this.forward, - this.done, - this.viewportFraction, - this.scrollDirection}); -} - -abstract class PageTransformer { - /// - final bool reverse; - - PageTransformer({this.reverse: false}); - - /// Return a transformed widget, based on child and TransformInfo - Widget transform(Widget child, TransformInfo info); -} - -typedef Widget PageTransformerBuilderCallback(Widget child, TransformInfo info); - -class PageTransformerBuilder extends PageTransformer { - final PageTransformerBuilderCallback builder; - - PageTransformerBuilder({bool reverse: false, @required this.builder}) - : assert(builder != null), - super(reverse: reverse); - - @override - Widget transform(Widget child, TransformInfo info) { - return builder(child, info); - } -} - -class TransformerPageController extends PageController { - final bool loop; - final int itemCount; - final bool reverse; - - TransformerPageController({ - int initialPage = 0, - bool keepPage = true, - double viewportFraction = 1.0, - this.loop: false, - this.itemCount, - this.reverse: false, - }) : super( - initialPage: TransformerPageController._getRealIndexFromRenderIndex( - initialPage ?? 0, loop, itemCount, reverse), - keepPage: keepPage, - viewportFraction: viewportFraction); - - int getRenderIndexFromRealIndex(num index) { - return _getRenderIndexFromRealIndex(index, loop, itemCount, reverse); - } - - int getRealItemCount() { - if (itemCount == 0) return 0; - return loop ? itemCount + kMaxValue : itemCount; - } - - static _getRenderIndexFromRealIndex( - num index, bool loop, int itemCount, bool reverse) { - if (itemCount == 0) return 0; - int renderIndex; - if (loop) { - renderIndex = index - kMiddleValue; - renderIndex = renderIndex % itemCount; - if (renderIndex < 0) { - renderIndex += itemCount; - } - } else { - renderIndex = index; - } - if (reverse) { - renderIndex = itemCount - renderIndex - 1; - } - - return renderIndex; - } - - double get realPage { - double page; - if (position.maxScrollExtent == null || position.minScrollExtent == null) { - page = 0.0; - } else { - page = super.page; - } - - return page; - } - - static _getRenderPageFromRealPage( - double page, bool loop, int itemCount, bool reverse) { - double renderPage; - if (loop) { - renderPage = page - kMiddleValue; - renderPage = renderPage % itemCount; - if (renderPage < 0) { - renderPage += itemCount; - } - } else { - renderPage = page; - } - if (reverse) { - renderPage = itemCount - renderPage - 1; - } - - return renderPage; - } - - double get page { - return loop - ? _getRenderPageFromRealPage(realPage, loop, itemCount, reverse) - : realPage; - } - - int getRealIndexFromRenderIndex(num index) { - return _getRealIndexFromRenderIndex(index, loop, itemCount, reverse); - } - - static int _getRealIndexFromRenderIndex( - num index, bool loop, int itemCount, bool reverse) { - int result = reverse ? (itemCount - index - 1) : index; - if (loop) { - result += kMiddleValue; - } - return result; - } -} - -class TransformerPageView extends StatefulWidget { - /// Create a `transformed` widget base on the widget that has been passed to the [PageTransformer.transform]. - /// See [TransformInfo] - /// - final PageTransformer transformer; - - /// Same as [PageView.scrollDirection] - /// - /// Defaults to [Axis.horizontal]. - final Axis scrollDirection; - - /// Same as [PageView.physics] - final ScrollPhysics physics; - - /// Set to false to disable page snapping, useful for custom scroll behavior. - /// Same as [PageView.pageSnapping] - final bool pageSnapping; - - /// Called whenever the page in the center of the viewport changes. - /// Same as [PageView.onPageChanged] - final ValueChanged onPageChanged; - - final IndexedWidgetBuilder itemBuilder; - - // See [IndexController.mode],[IndexController.next],[IndexController.previous] - final IndexController controller; - - /// Animation duration - final Duration duration; - - /// Animation curve - final Curve curve; - - final TransformerPageController pageController; - - /// Set true to open infinity loop mode. - final bool loop; - - /// This value is only valid when `pageController` is not set, - final int itemCount; - - /// This value is only valid when `pageController` is not set, - final double viewportFraction; - - /// If not set, it is controlled by this widget. - final int index; - - /// Creates a scrollable list that works page by page using widgets that are - /// created on demand. - /// - /// This constructor is appropriate for page views with a large (or infinite) - /// number of children because the builder is called only for those children - /// that are actually visible. - /// - /// Providing a non-null [itemCount] lets the [PageView] compute the maximum - /// scroll extent. - /// - /// [itemBuilder] will be called only with indices greater than or equal to - /// zero and less than [itemCount]. - TransformerPageView({ - Key key, - this.index, - Duration duration, - this.curve: Curves.ease, - this.viewportFraction: 1.0, - this.loop: false, - this.scrollDirection = Axis.horizontal, - this.physics, - this.pageSnapping = true, - this.onPageChanged, - this.controller, - this.transformer, - this.itemBuilder, - this.pageController, - @required this.itemCount, - }) : assert(itemCount != null), - assert(itemCount == 0 || itemBuilder != null || transformer != null), - this.duration = - duration ?? new Duration(milliseconds: kDefaultTransactionDuration), - super(key: key); - - factory TransformerPageView.children( - {Key key, - int index, - Duration duration, - Curve curve: Curves.ease, - double viewportFraction: 1.0, - bool loop: false, - Axis scrollDirection = Axis.horizontal, - ScrollPhysics physics, - bool pageSnapping = true, - ValueChanged onPageChanged, - IndexController controller, - PageTransformer transformer, - @required List children, - TransformerPageController pageController}) { - assert(children != null); - return new TransformerPageView( - itemCount: children.length, - itemBuilder: (BuildContext context, int index) { - return children[index]; - }, - pageController: pageController, - transformer: transformer, - pageSnapping: pageSnapping, - key: key, - index: index, - duration: duration, - curve: curve, - viewportFraction: viewportFraction, - scrollDirection: scrollDirection, - physics: physics, - onPageChanged: onPageChanged, - controller: controller, - ); - } - - @override - State createState() { - return new _TransformerPageViewState(); - } - - static int getRealIndexFromRenderIndex( - {bool reverse, int index, int itemCount, bool loop}) { - int initPage = reverse ? (itemCount - index - 1) : index; - if (loop) { - initPage += kMiddleValue; - } - return initPage; - } - - static PageController createPageController( - {bool reverse, - int index, - int itemCount, - bool loop, - double viewportFraction}) { - return new PageController( - initialPage: getRealIndexFromRenderIndex( - reverse: reverse, index: index, itemCount: itemCount, loop: loop), - viewportFraction: viewportFraction); - } -} - -class _TransformerPageViewState extends State { - Size _size; - int _activeIndex; - double _currentPixels; - bool _done = false; - - ///This value will not change until user end drag. - int _fromIndex; - - PageTransformer _transformer; - - TransformerPageController _pageController; - - Widget _buildItemNormal(BuildContext context, int index) { - int renderIndex = _pageController.getRenderIndexFromRealIndex(index); - Widget child = widget.itemBuilder(context, renderIndex); - return child; - } - - Widget _buildItem(BuildContext context, int index) { - return new AnimatedBuilder( - animation: _pageController, - builder: (BuildContext c, Widget w) { - int renderIndex = _pageController.getRenderIndexFromRealIndex(index); - Widget child; - if (widget.itemBuilder != null) { - child = widget.itemBuilder(context, renderIndex); - } - if (child == null) { - child = new Container(); - } - if (_size == null) { - return child ?? new Container(); - } - - double position; - - double page = _pageController.realPage; - - if (_transformer.reverse) { - position = page - index; - } else { - position = index - page; - } - position *= widget.viewportFraction; - - TransformInfo info = new TransformInfo( - index: renderIndex, - width: _size.width, - height: _size.height, - position: position.clamp(-1.0, 1.0), - activeIndex: - _pageController.getRenderIndexFromRealIndex(_activeIndex), - fromIndex: _fromIndex, - forward: _pageController.position.pixels - _currentPixels >= 0, - done: _done, - scrollDirection: widget.scrollDirection, - viewportFraction: widget.viewportFraction); - return _transformer.transform(child, info); - }); - } - - double _calcCurrentPixels() { - _currentPixels = _pageController.getRenderIndexFromRealIndex(_activeIndex) * - _pageController.position.viewportDimension * - widget.viewportFraction; - - // print("activeIndex:$_activeIndex , pix:$_currentPixels"); - - return _currentPixels; - } - - @override - Widget build(BuildContext context) { - IndexedWidgetBuilder builder = - _transformer == null ? _buildItemNormal : _buildItem; - Widget child = new PageView.builder( - itemBuilder: builder, - itemCount: _pageController.getRealItemCount(), - onPageChanged: _onIndexChanged, - controller: _pageController, - scrollDirection: widget.scrollDirection, - physics: widget.physics, - pageSnapping: widget.pageSnapping, - reverse: _pageController.reverse, - ); - if (_transformer == null) { - return child; - } - return new NotificationListener( - onNotification: (ScrollNotification notification) { - if (notification is ScrollStartNotification) { - _calcCurrentPixels(); - _done = false; - _fromIndex = _activeIndex; - } else if (notification is ScrollEndNotification) { - _calcCurrentPixels(); - _fromIndex = _activeIndex; - _done = true; - } - - return false; - }, - child: child); - } - - void _onIndexChanged(int index) { - _activeIndex = index; - if (widget.onPageChanged != null) { - widget.onPageChanged(_pageController.getRenderIndexFromRealIndex(index)); - } - } - - void _onGetSize(_) { - Size size; - if (context == null) { - onGetSize(size); - return; - } - RenderObject renderObject = context.findRenderObject(); - if (renderObject != null) { - Rect bounds = renderObject.paintBounds; - if (bounds != null) { - size = bounds.size; - } - } - _calcCurrentPixels(); - onGetSize(size); - } - - void onGetSize(Size size) { - if(mounted){ - setState(() { - _size = size; - }); - } - - } - - @override - void initState() { - _transformer = widget.transformer; - // int index = widget.index ?? 0; - _pageController = widget.pageController; - if (_pageController == null) { - _pageController = new TransformerPageController( - initialPage: widget.index, - itemCount: widget.itemCount, - loop: widget.loop, - reverse: - widget.transformer == null ? false : widget.transformer.reverse); - } - // int initPage = _getRealIndexFromRenderIndex(index); - // _pageController = new PageController(initialPage: initPage,viewportFraction: widget.viewportFraction); - _fromIndex = _activeIndex = _pageController.initialPage; - - _controller = getNotifier(); - if (_controller != null) { - _controller.addListener(onChangeNotifier); - } - super.initState(); - } - - @override - void didUpdateWidget(TransformerPageView oldWidget) { - _transformer = widget.transformer; - int index = widget.index ?? 0; - bool created = false; - if (_pageController != widget.pageController) { - if (widget.pageController != null) { - _pageController = widget.pageController; - } else { - created = true; - _pageController = new TransformerPageController( - initialPage: widget.index, - itemCount: widget.itemCount, - loop: widget.loop, - reverse: widget.transformer == null - ? false - : widget.transformer.reverse); - } - } - - if (_pageController.getRenderIndexFromRealIndex(_activeIndex) != index) { - _fromIndex = _activeIndex = _pageController.initialPage; - if (!created) { - int initPage = _pageController.getRealIndexFromRenderIndex(index); - _pageController.animateToPage(initPage, - duration: widget.duration, curve: widget.curve); - } - } - if (_transformer != null) - WidgetsBinding.instance.addPostFrameCallback(_onGetSize); - - if (_controller != getNotifier()) { - if (_controller != null) { - _controller.removeListener(onChangeNotifier); - } - _controller = getNotifier(); - if (_controller != null) { - _controller.addListener(onChangeNotifier); - } - } - super.didUpdateWidget(oldWidget); - } - - @override - void didChangeDependencies() { - if (_transformer != null) - WidgetsBinding.instance.addPostFrameCallback(_onGetSize); - super.didChangeDependencies(); - } - - ChangeNotifier getNotifier() { - return widget.controller; - } - - int _calcNextIndex(bool next) { - int currentIndex = _activeIndex; - if (_pageController.reverse) { - if (next) { - currentIndex--; - } else { - currentIndex++; - } - } else { - if (next) { - currentIndex++; - } else { - currentIndex--; - } - } - - if (!_pageController.loop) { - if (currentIndex >= _pageController.itemCount) { - currentIndex = 0; - } else if (currentIndex < 0) { - currentIndex = _pageController.itemCount - 1; - } - } - - return currentIndex; - } - - void onChangeNotifier() { - int event = widget.controller.event; - int index; - switch (event) { - case IndexController.MOVE: - { - index = _pageController - .getRealIndexFromRenderIndex(widget.controller.index); - } - break; - case IndexController.PREVIOUS: - case IndexController.NEXT: - { - index = _calcNextIndex(event == IndexController.NEXT); - } - break; - default: - //ignore this event - return; - } - if (widget.controller.animation) { - _pageController - .animateToPage(index, - duration: widget.duration, curve: widget.curve ?? Curves.ease) - .whenComplete(widget.controller.complete); - } else { - _pageController.jumpToPage(index); - widget.controller.complete(); - } - } - - ChangeNotifier _controller; - - void dispose() { - super.dispose(); - if (_controller != null) { - _controller.removeListener(onChangeNotifier); - } - } -} +export 'src/another_transformer_page_view.dart'; +export 'src/index_controller.dart'; +export 'src/parallax.dart'; diff --git a/lib/src/another_transformer_page_view.dart b/lib/src/another_transformer_page_view.dart new file mode 100644 index 0000000..7b5972b --- /dev/null +++ b/lib/src/another_transformer_page_view.dart @@ -0,0 +1,608 @@ +library another_transformer_page_view; + +import 'package:flutter/widgets.dart'; + +import 'index_controller.dart'; + +/// +/// NOTICE:: +/// +/// In order to make package smaller,currently we're not supporting any build-in page transformers +/// You can find build in transforms here: +/// +/// +/// + +const int kMaxValue = 2000000000; +const int kMiddleValue = 1000000000; + +/// Default auto play transition duration (in millisecond) +const int kDefaultTransactionDuration = 300; + +class TransformInfo { + /// The `width` of the `TransformerPageView` + final double? width; + + /// The `height` of the `TransformerPageView` + final double? height; + + /// The `position` of the widget pass to [PageTransformer.transform] + /// A `position` describes how visible the widget is. + /// The widget in the center of the screen' which is full visible, position is 0.0. + /// The widge in the left ,may be hidden, of the screen's position is less than 0.0, -1.0 when out of the screen. + /// The widge in the right ,may be hidden, of the screen's position is greater than 0.0, 1.0 when out of the screen + /// + /// + final double? position; + + /// The `index` of the widget pass to [PageTransformer.transform] + final int? index; + + /// The `activeIndex` of the PageView + final int? activeIndex; + + /// The `activeIndex` of the PageView, from user start to swipe + /// It will change when user end drag + final int? fromIndex; + + /// Next `index` is greater than this `index` + final bool? forward; + + /// User drag is done. + final bool? done; + + /// Same as [TransformerPageView.viewportFraction] + final double? viewportFraction; + + /// Copy from [TransformerPageView.scrollDirection] + final Axis? scrollDirection; + + TransformInfo( + {this.index, + this.position, + this.width, + this.height, + this.activeIndex, + this.fromIndex, + this.forward, + this.done, + this.viewportFraction, + this.scrollDirection}); +} + +abstract class PageTransformer { + /// + final bool reverse; + + PageTransformer({this.reverse: false}); + + /// Return a transformed widget, based on child and TransformInfo + Widget transform(Widget child, TransformInfo info); +} + +typedef Widget PageTransformerBuilderCallback(Widget child, TransformInfo info); + +class PageTransformerBuilder extends PageTransformer { + final PageTransformerBuilderCallback builder; + + PageTransformerBuilder({bool reverse: false, required this.builder}) + : super(reverse: reverse); + + @override + Widget transform(Widget child, TransformInfo info) { + return builder(child, info); + } +} + +class TransformerPageController extends PageController { + final bool loop; + final int? itemCount; + final bool reverse; + + TransformerPageController({ + int? initialPage = 0, + bool keepPage = true, + double viewportFraction = 1.0, + this.loop: false, + this.itemCount, + this.reverse: false, + }) : super( + initialPage: TransformerPageController._getRealIndexFromRenderIndex( + initialPage ?? 0, loop, itemCount, reverse)!, + keepPage: keepPage, + viewportFraction: viewportFraction); + + int? getRenderIndexFromRealIndex(int index) { + return _getRenderIndexFromRealIndex(index, loop, itemCount, reverse); + } + + int? getRealItemCount() { + if (itemCount == 0) return 0; + return loop ? itemCount! + kMaxValue : itemCount; + } + + static _getRenderIndexFromRealIndex( + int index, bool loop, int? itemCount, bool reverse) { + if (itemCount == 0) return 0; + int? renderIndex; + if (loop) { + renderIndex = index - kMiddleValue; + renderIndex = renderIndex % itemCount!; + if (renderIndex < 0) { + renderIndex += itemCount; + } + } else { + renderIndex = index; + } + if (reverse) { + renderIndex = itemCount! - renderIndex - 1; + } + + return renderIndex; + } + + double? get realPage { + return super.page; + } + + static _getRenderPageFromRealPage( + double? page, bool loop, int? itemCount, bool reverse) { + double? renderPage; + if (loop) { + renderPage = page! - kMiddleValue; + renderPage = renderPage % itemCount!; + if (renderPage < 0) { + renderPage += itemCount; + } + } else { + renderPage = page; + } + if (reverse) { + renderPage = itemCount! - renderPage! - 1; + } + + return renderPage; + } + + double? get page { + return loop + ? _getRenderPageFromRealPage(realPage, loop, itemCount, reverse) + : realPage; + } + + int? getRealIndexFromRenderIndex(int? index) { + return _getRealIndexFromRenderIndex(index, loop, itemCount, reverse); + } + + static int? _getRealIndexFromRenderIndex( + int? index, bool loop, int? itemCount, bool reverse) { + int result = reverse ? itemCount! - index! - 1 : index!; + if (loop) { + result += kMiddleValue; + } + return result; + } +} + +class TransformerPageView extends StatefulWidget { + /// Create a `transformed` widget base on the widget that has been passed to the [PageTransformer.transform]. + /// See [TransformInfo] + /// + final PageTransformer? transformer; + + /// Same as [PageView.scrollDirection] + /// + /// Defaults to [Axis.horizontal]. + final Axis scrollDirection; + + /// Same as [PageView.physics] + final ScrollPhysics? physics; + + /// Set to false to disable page snapping, useful for custom scroll behavior. + /// Same as [PageView.pageSnapping] + final bool pageSnapping; + + /// Called whenever the page in the center of the viewport changes. + /// Same as [PageView.onPageChanged] + final ValueChanged? onPageChanged; + + final IndexedWidgetBuilder? itemBuilder; + + // See [IndexController.mode],[IndexController.next],[IndexController.previous] + final IndexController? controller; + + /// Animation duration + final Duration duration; + + /// Animation curve + final Curve curve; + + final TransformerPageController? pageController; + + /// Set true to open infinity loop mode. + final bool loop; + + /// This value is only valid when `pageController` is not set, + final int itemCount; + + /// This value is only valid when `pageController` is not set, + final double viewportFraction; + + /// If not set, it is controlled by this widget. + final int? index; + + /// Creates a scrollable list that works page by page using widgets that are + /// created on demand. + /// + /// This constructor is appropriate for page views with a large (or infinite) + /// number of children because the builder is called only for those children + /// that are actually visible. + /// + /// Providing a non-null [itemCount] lets the [PageView] compute the maximum + /// scroll extent. + /// + /// [itemBuilder] will be called only with indices greater than or equal to + /// zero and less than [itemCount]. + TransformerPageView({ + Key? key, + this.index, + Duration? duration, + this.curve: Curves.ease, + this.viewportFraction: 1.0, + this.loop: false, + this.scrollDirection = Axis.horizontal, + this.physics, + this.pageSnapping = true, + this.onPageChanged, + this.controller, + this.transformer, + this.itemBuilder, + this.pageController, + required this.itemCount, + }) : assert(itemCount == 0 || itemBuilder != null || transformer != null), + this.duration = + duration ?? new Duration(milliseconds: kDefaultTransactionDuration), + super(key: key); + + factory TransformerPageView.children( + {Key? key, + int? index, + Duration? duration, + Curve curve: Curves.ease, + double viewportFraction: 1.0, + bool loop: false, + Axis scrollDirection = Axis.horizontal, + ScrollPhysics? physics, + bool pageSnapping = true, + ValueChanged? onPageChanged, + IndexController? controller, + PageTransformer? transformer, + required List children, + TransformerPageController? pageController}) { + return new TransformerPageView( + itemCount: children.length, + itemBuilder: (BuildContext context, int index) { + return children[index]; + }, + pageController: pageController, + transformer: transformer, + pageSnapping: pageSnapping, + key: key, + index: index, + duration: duration, + curve: curve, + viewportFraction: viewportFraction, + scrollDirection: scrollDirection, + physics: physics, + onPageChanged: onPageChanged, + controller: controller, + ); + } + + @override + State createState() { + return new _TransformerPageViewState(); + } + + static int? getRealIndexFromRenderIndex( + {required bool reverse, int? index, int? itemCount, required bool loop}) { + int initPage = reverse ? (itemCount! - index! - 1) : index!; + if (loop) { + initPage += kMiddleValue; + } + return initPage; + } + + static PageController createPageController( + {required bool reverse, + int? index, + int? itemCount, + required bool loop, + required double viewportFraction}) { + return new PageController( + initialPage: getRealIndexFromRenderIndex( + reverse: reverse, index: index, itemCount: itemCount, loop: loop)!, + viewportFraction: viewportFraction); + } +} + +class _TransformerPageViewState extends State { + Size? _size; + late int _activeIndex; + double? _currentPixels; + bool _done = false; + + ///This value will not change until user end drag. + int? _fromIndex; + + PageTransformer? _transformer; + + TransformerPageController? _pageController; + + Widget _buildItemNormal(BuildContext context, int index) { + int renderIndex = _pageController!.getRenderIndexFromRealIndex(index)!; + Widget child = widget.itemBuilder!(context, renderIndex); + return child; + } + + Widget _buildItem(BuildContext context, int index) { + return new AnimatedBuilder( + animation: _pageController!, + builder: (BuildContext c, Widget? w) { + int? renderIndex = + _pageController!.getRenderIndexFromRealIndex(index); + Widget? child; + if (widget.itemBuilder != null) { + child = widget.itemBuilder!(context, renderIndex!); + } + if (child == null) { + child = new Container(); + } + if (_size == null) { + return child; + } + + double position; + + double? page = _pageController!.realPage; + + if (_transformer!.reverse) { + position = page! - index; + } else { + position = index - page!; + } + position *= widget.viewportFraction; + + TransformInfo info = new TransformInfo( + index: renderIndex, + width: _size!.width, + height: _size!.height, + position: position.clamp(-1.0, 1.0), + activeIndex: + _pageController!.getRenderIndexFromRealIndex(_activeIndex), + fromIndex: _fromIndex, + forward: _pageController!.position.pixels - _currentPixels! >= 0, + done: _done, + scrollDirection: widget.scrollDirection, + viewportFraction: widget.viewportFraction); + return _transformer!.transform(child, info); + }); + } + + double? _calcCurrentPixels() { + _currentPixels = + _pageController!.getRenderIndexFromRealIndex(_activeIndex)! * + _pageController!.position.viewportDimension * + widget.viewportFraction; + + // print("activeIndex:$_activeIndex , pix:$_currentPixels"); + + return _currentPixels; + } + + @override + Widget build(BuildContext context) { + IndexedWidgetBuilder builder = + _transformer == null ? _buildItemNormal : _buildItem; + Widget child = new PageView.builder( + itemBuilder: builder, + itemCount: _pageController!.getRealItemCount(), + onPageChanged: _onIndexChanged, + controller: _pageController, + scrollDirection: widget.scrollDirection, + physics: widget.physics, + pageSnapping: widget.pageSnapping, + reverse: _pageController!.reverse, + ); + if (_transformer == null) { + return child; + } + return new NotificationListener( + onNotification: (ScrollNotification notification) { + if (notification is ScrollStartNotification) { + _calcCurrentPixels(); + _done = false; + _fromIndex = _activeIndex; + } else if (notification is ScrollEndNotification) { + _calcCurrentPixels(); + _fromIndex = _activeIndex; + _done = true; + } + + return false; + }, + child: child); + } + + void _onIndexChanged(int index) { + _activeIndex = index; + if (widget.onPageChanged != null) { + widget + .onPageChanged!(_pageController!.getRenderIndexFromRealIndex(index)); + } + } + + void _onGetSize(_) { + Size? size; + RenderObject? renderObject = context.findRenderObject(); + if (renderObject != null) { + Rect bounds = renderObject.paintBounds; + + size = bounds.size; + } + _calcCurrentPixels(); + onGetSize(size); + } + + void onGetSize(Size? size) { + if (mounted) { + setState(() { + _size = size; + }); + } + } + + @override + void initState() { + _transformer = widget.transformer; + // int index = widget.index ?? 0; + _pageController = widget.pageController; + if (_pageController == null) { + _pageController = new TransformerPageController( + initialPage: widget.index, + itemCount: widget.itemCount, + loop: widget.loop, + reverse: + widget.transformer == null ? false : widget.transformer!.reverse); + } + // int initPage = _getRealIndexFromRenderIndex(index); + // _pageController = new PageController(initialPage: initPage,viewportFraction: widget.viewportFraction); + _fromIndex = _activeIndex = _pageController!.initialPage; + + _controller = getNotifier(); + if (_controller != null) { + _controller!.addListener(onChangeNotifier); + } + super.initState(); + } + + @override + void didUpdateWidget(TransformerPageView oldWidget) { + _transformer = widget.transformer; + int index = widget.index ?? 0; + bool created = false; + if (_pageController != widget.pageController) { + if (widget.pageController != null) { + _pageController = widget.pageController; + } else { + created = true; + _pageController = new TransformerPageController( + initialPage: widget.index, + itemCount: widget.itemCount, + loop: widget.loop, + reverse: widget.transformer == null + ? false + : widget.transformer!.reverse); + } + } + + if (_pageController!.getRenderIndexFromRealIndex(_activeIndex) != index) { + _fromIndex = _activeIndex = _pageController!.initialPage; + if (!created) { + int initPage = _pageController!.getRealIndexFromRenderIndex(index)!; + _pageController!.animateToPage(initPage, + duration: widget.duration, curve: widget.curve); + } + } + if (_transformer != null) + WidgetsBinding.instance!.addPostFrameCallback(_onGetSize); + + if (_controller != getNotifier()) { + if (_controller != null) { + _controller!.removeListener(onChangeNotifier); + } + _controller = getNotifier(); + if (_controller != null) { + _controller!.addListener(onChangeNotifier); + } + } + super.didUpdateWidget(oldWidget); + } + + @override + void didChangeDependencies() { + if (_transformer != null) + WidgetsBinding.instance!.addPostFrameCallback(_onGetSize); + super.didChangeDependencies(); + } + + ChangeNotifier? getNotifier() { + return widget.controller; + } + + int _calcNextIndex(bool next) { + int? currentIndex = _activeIndex; + if (_pageController!.reverse) { + if (next) { + currentIndex--; + } else { + currentIndex++; + } + } else { + if (next) { + currentIndex++; + } else { + currentIndex--; + } + } + + if (!_pageController!.loop) { + if (currentIndex >= _pageController!.itemCount!) { + currentIndex = 0; + } else if (currentIndex < 0) { + currentIndex = _pageController!.itemCount! - 1; + } + } + + return currentIndex; + } + + void onChangeNotifier() { + int? event = widget.controller!.event; + int? index; + switch (event) { + case IndexController.MOVE: + { + index = _pageController! + .getRealIndexFromRenderIndex(widget.controller!.index); + } + break; + case IndexController.PREVIOUS: + case IndexController.NEXT: + { + index = _calcNextIndex(event == IndexController.NEXT); + } + break; + default: + //ignore this event + return; + } + if (widget.controller!.animation) { + _pageController! + .animateToPage(index!, duration: widget.duration, curve: widget.curve) + .whenComplete(widget.controller!.complete); + } else { + _pageController!.jumpToPage(index!); + widget.controller!.complete(); + } + } + + ChangeNotifier? _controller; + + void dispose() { + super.dispose(); + if (_controller != null) { + _controller!.removeListener(onChangeNotifier); + } + } +} diff --git a/lib/index_controller.dart b/lib/src/index_controller.dart similarity index 81% rename from lib/index_controller.dart rename to lib/src/index_controller.dart index 461278f..fde7b10 100644 --- a/lib/index_controller.dart +++ b/lib/src/index_controller.dart @@ -8,14 +8,14 @@ class IndexController extends ChangeNotifier { static const int PREVIOUS = -1; static const int MOVE = 0; - Completer _completer; + late Completer _completer; - int index; - bool animation; - int event; + int? index; + late bool animation; + int? event; Future move(int index, {bool animation: true}) { - this.animation = animation ?? true; + this.animation = animation; this.index = index; this.event = MOVE; _completer = new Completer(); @@ -25,7 +25,7 @@ class IndexController extends ChangeNotifier { Future next({bool animation: true}) { this.event = NEXT; - this.animation = animation ?? true; + this.animation = animation; _completer = new Completer(); notifyListeners(); return _completer.future; @@ -33,7 +33,7 @@ class IndexController extends ChangeNotifier { Future previous({bool animation: true}) { this.event = PREVIOUS; - this.animation = animation ?? true; + this.animation = animation; _completer = new Completer(); notifyListeners(); return _completer.future; diff --git a/lib/parallax.dart b/lib/src/parallax.dart similarity index 76% rename from lib/parallax.dart rename to lib/src/parallax.dart index 208356b..1aacae6 100644 --- a/lib/parallax.dart +++ b/lib/src/parallax.dart @@ -1,5 +1,6 @@ import 'package:flutter/widgets.dart'; -import 'package:another_transformer_page_view/another_transformer_page_view.dart'; + +import 'another_transformer_page_view.dart'; typedef void PaintCallback(Canvas canvas, Size siz); @@ -12,23 +13,23 @@ class ColorPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { - int index = info.fromIndex; + int index = info.fromIndex!; _paint.color = colors[index]; canvas.drawRect( new Rect.fromLTWH(0.0, 0.0, size.width, size.height), _paint); - if (info.done) { + if (info.done!) { return; } int alpha; int color; double opacity; - double position = info.position; - if (info.forward) { + double? position = info.position; + if (info.forward!) { if (index < colors.length - 1) { color = colors[index + 1].value & 0x00ffffff; - opacity = (position <= 0 - ? (-position / info.viewportFraction) - : 1 - position / info.viewportFraction); + opacity = (position! <= 0 + ? (-position / info.viewportFraction!) + : 1 - position / info.viewportFraction!); if (opacity > 1) { opacity -= 1.0; } @@ -44,9 +45,9 @@ class ColorPainter extends CustomPainter { } else { if (index > 0) { color = colors[index - 1].value & 0x00ffffff; - opacity = (position > 0 - ? position / info.viewportFraction - : (1 + position / info.viewportFraction)); + opacity = (position! > 0 + ? position / info.viewportFraction! + : (1 + position / info.viewportFraction!)); if (opacity > 1) { opacity -= 1.0; } @@ -88,9 +89,9 @@ class ParallaxColor extends StatefulWidget { final TransformInfo info; ParallaxColor({ - @required this.colors, - @required this.info, - @required this.child, + required this.colors, + required this.info, + required this.child, }); @override @@ -106,12 +107,10 @@ class ParallaxContainer extends StatelessWidget { final double opacityFactor; ParallaxContainer( - {@required this.child, - @required this.position, + {required this.child, + required this.position, this.translationFactor: 100.0, - this.opacityFactor: 1.0}) - : assert(position != null), - assert(translationFactor != null); + this.opacityFactor: 1.0}); @override Widget build(BuildContext context) { @@ -129,9 +128,9 @@ class ParallaxImage extends StatelessWidget { final Image image; final double imageFactor; - ParallaxImage.asset(String name, {double position, this.imageFactor: 0.3}) - : assert(imageFactor != null), - image = Image.asset(name, + ParallaxImage.asset(String name, + {required double position, this.imageFactor: 0.3}) + : image = Image.asset(name, fit: BoxFit.cover, alignment: FractionalOffset( 0.5 + position * imageFactor, diff --git a/test/transformer_page_view_test.dart b/test/transformer_page_view_test.dart index 921445d..6069f58 100644 --- a/test/transformer_page_view_test.dart +++ b/test/transformer_page_view_test.dart @@ -1,3 +1,4 @@ +import 'package:another_transformer_page_view/src/index_controller.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart';