|
|
|
// Package transformer_page_view:
|
|
|
|
// https://pub.dartlang.org/packages/transformer_page_view
|
|
|
|
|
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
|
|
|
|
class IndexController extends ChangeNotifier {
|
|
|
|
// ignore: constant_identifier_names
|
|
|
|
static const int NEXT = 1;
|
|
|
|
// ignore: constant_identifier_names
|
|
|
|
static const int PREVIOUS = -1;
|
|
|
|
// ignore: constant_identifier_names
|
|
|
|
static const int MOVE = 0;
|
|
|
|
|
|
|
|
Completer<void> _completer;
|
|
|
|
|
|
|
|
int index;
|
|
|
|
bool animation;
|
|
|
|
int event;
|
|
|
|
|
|
|
|
Future move(int index, {bool animation = true}) {
|
|
|
|
this.animation = animation ?? true;
|
|
|
|
this.index = index;
|
|
|
|
event = MOVE;
|
|
|
|
_completer = Completer();
|
|
|
|
notifyListeners();
|
|
|
|
return _completer.future;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future next({bool animation = true}) {
|
|
|
|
event = NEXT;
|
|
|
|
this.animation = animation ?? true;
|
|
|
|
_completer = Completer();
|
|
|
|
notifyListeners();
|
|
|
|
return _completer.future;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future previous({bool animation = true}) {
|
|
|
|
event = PREVIOUS;
|
|
|
|
this.animation = animation ?? true;
|
|
|
|
_completer = Completer();
|
|
|
|
notifyListeners();
|
|
|
|
return _completer.future;
|
|
|
|
}
|
|
|
|
|
|
|
|
void complete() {
|
|
|
|
if (!_completer.isCompleted) {
|
|
|
|
_completer.complete();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef PaintCallback = void Function(Canvas canvas, Size siz);
|
|
|
|
|
|
|
|
class ColorPainter extends CustomPainter {
|
|
|
|
final Paint _paint;
|
|
|
|
final TransformInfo info;
|
|
|
|
final List<Color> colors;
|
|
|
|
|
|
|
|
ColorPainter(this._paint, this.info, this.colors);
|
|
|
|
|
|
|
|
@override
|
|
|
|
void paint(Canvas canvas, Size size) {
|
|
|
|
int index = info.fromIndex;
|
|
|
|
_paint.color = colors[index];
|
|
|
|
canvas.drawRect(Rect.fromLTWH(0.0, 0.0, size.width, size.height), _paint);
|
|
|
|
if (info.done) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int alpha;
|
|
|
|
int color;
|
|
|
|
double opacity;
|
|
|
|
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);
|
|
|
|
if (opacity > 1) {
|
|
|
|
opacity -= 1.0;
|
|
|
|
}
|
|
|
|
if (opacity < 0) {
|
|
|
|
opacity += 1.0;
|
|
|
|
}
|
|
|
|
alpha = (0xff * opacity).toInt();
|
|
|
|
|
|
|
|
_paint.color = Color((alpha << 24) | color);
|
|
|
|
canvas.drawRect(
|
|
|
|
Rect.fromLTWH(0.0, 0.0, size.width, size.height), _paint);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (index > 0) {
|
|
|
|
color = colors[index - 1].value & 0x00ffffff;
|
|
|
|
opacity = (position > 0
|
|
|
|
? position / info.viewportFraction
|
|
|
|
: (1 + position / info.viewportFraction));
|
|
|
|
if (opacity > 1) {
|
|
|
|
opacity -= 1.0;
|
|
|
|
}
|
|
|
|
if (opacity < 0) {
|
|
|
|
opacity += 1.0;
|
|
|
|
}
|
|
|
|
alpha = (0xff * opacity).toInt();
|
|
|
|
|
|
|
|
_paint.color = Color((alpha << 24) | color);
|
|
|
|
canvas.drawRect(
|
|
|
|
Rect.fromLTWH(0.0, 0.0, size.width, size.height), _paint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
bool shouldRepaint(ColorPainter oldDelegate) {
|
|
|
|
return oldDelegate.info != info;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _ParallaxColorState extends State<ParallaxColor> {
|
|
|
|
Paint paint = Paint();
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return CustomPaint(
|
|
|
|
painter: ColorPainter(paint, widget.info, widget.colors),
|
|
|
|
child: widget.child,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ParallaxColor extends StatefulWidget {
|
|
|
|
final Widget child;
|
|
|
|
|
|
|
|
final List<Color> colors;
|
|
|
|
|
|
|
|
final TransformInfo info;
|
|
|
|
|
|
|
|
const ParallaxColor({
|
|
|
|
@required this.colors,
|
|
|
|
@required this.info,
|
|
|
|
@required this.child,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<StatefulWidget> createState() {
|
|
|
|
return _ParallaxColorState();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ParallaxContainer extends StatelessWidget {
|
|
|
|
final Widget child;
|
|
|
|
final double position;
|
|
|
|
final double translationFactor;
|
|
|
|
final double opacityFactor;
|
|
|
|
|
|
|
|
const ParallaxContainer(
|
|
|
|
{@required this.child,
|
|
|
|
@required this.position,
|
|
|
|
this.translationFactor = 100.0,
|
|
|
|
this.opacityFactor = 1.0})
|
|
|
|
: assert(position != null),
|
|
|
|
assert(translationFactor != null);
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Opacity(
|
|
|
|
opacity: (1 - position.abs()).clamp(0.0, 1.0) * opacityFactor as double,
|
|
|
|
child: Transform.translate(
|
|
|
|
offset: Offset(position * translationFactor, 0.0),
|
|
|
|
child: child,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
fit: BoxFit.cover,
|
|
|
|
alignment: FractionalOffset(
|
|
|
|
0.5 + position * imageFactor,
|
|
|
|
0.5,
|
|
|
|
));
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// 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 PageTransformerBuilderCallback = Widget Function(
|
|
|
|
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(int index) {
|
|
|
|
return _getRenderIndexFromRealIndex(index, loop, itemCount, reverse);
|
|
|
|
}
|
|
|
|
|
|
|
|
int getRealItemCount() {
|
|
|
|
if (itemCount == 0) return 0;
|
|
|
|
return loop ? itemCount + kMaxValue : itemCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _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 {
|
|
|
|
double page;
|
|
|
|
if (position.maxScrollExtent == null || position.minScrollExtent == null) {
|
|
|
|
page = 0.0;
|
|
|
|
} else {
|
|
|
|
page = super.page;
|
|
|
|
}
|
|
|
|
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
|
|
|
static double _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;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
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<int> 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].
|
|
|
|
const 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),
|
|
|
|
duration = duration ??
|
|
|
|
const 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<int> onPageChanged,
|
|
|
|
IndexController controller,
|
|
|
|
PageTransformer transformer,
|
|
|
|
@required List<Widget> children,
|
|
|
|
TransformerPageController pageController}) {
|
|
|
|
assert(children != null);
|
|
|
|
return TransformerPageView(
|
|
|
|
itemCount: children.length,
|
|
|
|
itemBuilder: (context, 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<StatefulWidget> createState() {
|
|
|
|
return _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 PageController(
|
|
|
|
initialPage: getRealIndexFromRenderIndex(
|
|
|
|
reverse: reverse, index: index, itemCount: itemCount, loop: loop),
|
|
|
|
viewportFraction: viewportFraction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _TransformerPageViewState extends State<TransformerPageView> {
|
|
|
|
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 AnimatedBuilder(
|
|
|
|
animation: _pageController,
|
|
|
|
builder: (c, w) {
|
|
|
|
int renderIndex = _pageController.getRenderIndexFromRealIndex(index);
|
|
|
|
Widget child;
|
|
|
|
if (widget.itemBuilder != null) {
|
|
|
|
child = widget.itemBuilder(context, renderIndex);
|
|
|
|
}
|
|
|
|
child ??= Container();
|
|
|
|
if (_size == null) {
|
|
|
|
return child ?? Container();
|
|
|
|
}
|
|
|
|
|
|
|
|
double position;
|
|
|
|
|
|
|
|
double page = _pageController.realPage;
|
|
|
|
|
|
|
|
if (_transformer.reverse) {
|
|
|
|
position = page - index;
|
|
|
|
} else {
|
|
|
|
position = index - page;
|
|
|
|
}
|
|
|
|
position *= widget.viewportFraction;
|
|
|
|
|
|
|
|
TransformInfo info = TransformInfo(
|
|
|
|
index: renderIndex,
|
|
|
|
width: _size.width,
|
|
|
|
height: _size.height,
|
|
|
|
position: position.clamp(-1.0, 1.0) as double,
|
|
|
|
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 = 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 NotificationListener(
|
|
|
|
// ignore: avoid_types_on_closure_parameters
|
|
|
|
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(dynamic _) {
|
|
|
|
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;
|
|
|
|
_pageController ??= 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 = 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;
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
super.dispose();
|
|
|
|
if (_controller != null) {
|
|
|
|
_controller.removeListener(onChangeNotifier);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|