我在我的一个项目中遇到了同样的问题.因此,您可以在项目中导入此文件,而不是使用默认的PageViewBuilder,我创建了我的CustomPageViewBuilder
.还有,你必须像这样使用pagecontroller
号CustomPageController pageController = CustomPageController();
import 'dart:math' as math;
import 'package:flutter/foundation.dart'
show clampDouble, precisionErrorTolerance;
import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class CustomPageController extends ScrollController {
CustomPageController({
this.initialPage = 0,
this.keepPage = true,
this.viewportFraction = 1.0,
}) : assert(viewportFraction > 0.0);
final int initialPage;
final bool keepPage;
final double viewportFraction;
double? get page {
assert(
positions.isNotEmpty,
'CustomPageController.page cannot be accessed before a CustomPageView is built with it.',
);
assert(
positions.length == 1,
'The page property cannot be read when multiple CustomPageViews are attached to '
'the same CustomPageController.',
);
final _PagePosition position = this.position as _PagePosition;
return position.page;
}
Future<void> animateToPage(
int page, {
required Duration duration,
required Curve curve,
}) {
final _PagePosition position = this.position as _PagePosition;
if (position._cachedPage != null) {
position._cachedPage = page.toDouble();
return Future<void>.value();
}
return position.animateTo(
position.getPixelsFromPage(page.toDouble()),
duration: duration,
curve: curve,
);
}
void jumpToPage(int page) {
final _PagePosition position = this.position as _PagePosition;
if (position._cachedPage != null) {
position._cachedPage = page.toDouble();
return;
}
position.jumpTo(position.getPixelsFromPage(page.toDouble()));
}
Future<void> nextPage({required Duration duration, required Curve curve}) {
return animateToPage(page!.round() + 1, duration: duration, curve: curve);
}
Future<void> previousPage(
{required Duration duration, required Curve curve}) {
return animateToPage(page!.round() - 1, duration: duration, curve: curve);
}
@override
ScrollPosition createScrollPosition(ScrollPhysics physics,
ScrollContext context, ScrollPosition? oldPosition) {
return _PagePosition(
physics: physics,
context: context,
initialPage: initialPage,
keepPage: keepPage,
viewportFraction: viewportFraction,
oldPosition: oldPosition,
);
}
@override
void attach(ScrollPosition position) {
super.attach(position);
final _PagePosition pagePosition = position as _PagePosition;
pagePosition.viewportFraction = viewportFraction;
}
}
class PageMetrics extends FixedScrollMetrics {
PageMetrics({
required super.minScrollExtent,
required super.maxScrollExtent,
required super.pixels,
required super.viewportDimension,
required super.axisDirection,
required this.viewportFraction,
required super.devicePixelRatio,
});
@override
PageMetrics copyWith({
double? minScrollExtent,
double? maxScrollExtent,
double? pixels,
double? viewportDimension,
AxisDirection? axisDirection,
double? viewportFraction,
double? devicePixelRatio,
}) {
return PageMetrics(
minScrollExtent: minScrollExtent ??
(hasContentDimensions ? this.minScrollExtent : null),
maxScrollExtent: maxScrollExtent ??
(hasContentDimensions ? this.maxScrollExtent : null),
pixels: pixels ?? (hasPixels ? this.pixels : null),
viewportDimension: viewportDimension ??
(hasViewportDimension ? this.viewportDimension : null),
axisDirection: axisDirection ?? this.axisDirection,
viewportFraction: viewportFraction ?? this.viewportFraction,
devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
);
}
double? get page {
return math.max(
0.0, clampDouble(pixels, minScrollExtent, maxScrollExtent)) /
math.max(1.0, viewportDimension * viewportFraction);
}
final double viewportFraction;
}
class _PagePosition extends ScrollPositionWithSingleContext
implements PageMetrics {
_PagePosition({
required super.physics,
required super.context,
this.initialPage = 0,
bool keepPage = true,
double viewportFraction = 1.0,
super.oldPosition,
}) : assert(viewportFraction > 0.0),
_viewportFraction = viewportFraction,
_pageToUseOnStartup = initialPage.toDouble(),
super(
initialPixels: null,
keepScrollOffset: keepPage,
);
final int initialPage;
double _pageToUseOnStartup;
double? _cachedPage;
@override
Future<void> ensureVisible(
RenderObject object, {
double alignment = 0.0,
Duration duration = Duration.zero,
Curve curve = Curves.ease,
ScrollPositionAlignmentPolicy alignmentPolicy =
ScrollPositionAlignmentPolicy.explicit,
RenderObject? targetRenderObject,
}) {
return super.ensureVisible(
object,
alignment: alignment,
duration: duration,
curve: curve,
alignmentPolicy: alignmentPolicy,
);
}
@override
double get viewportFraction => _viewportFraction;
double _viewportFraction;
set viewportFraction(double value) {
if (_viewportFraction == value) {
return;
}
final double? oldPage = page;
_viewportFraction = value;
if (oldPage != null) {
forcePixels(getPixelsFromPage(oldPage));
}
}
double get _initialPageOffset =>
math.max(0, viewportDimension * (viewportFraction - 1) / 2);
double getPageFromPixels(double pixels, double viewportDimension) {
assert(viewportDimension > 0.0);
final double actual = math.max(0.0, pixels - _initialPageOffset) /
(viewportDimension * viewportFraction);
final double round = actual.roundToDouble();
if ((actual - round).abs() < precisionErrorTolerance) {
return round;
}
return actual;
}
double getPixelsFromPage(double page) {
return page * viewportDimension * viewportFraction + _initialPageOffset;
}
@override
double? get page {
assert(
!hasPixels || hasContentDimensions,
'Page value is only available after content dimensions are established.',
);
return !hasPixels || !hasContentDimensions
? null
: _cachedPage ??
getPageFromPixels(
clampDouble(pixels, minScrollExtent, maxScrollExtent),
viewportDimension);
}
@override
void saveScrollOffset() {
PageStorage.maybeOf(context.storageContext)?.writeState(
context.storageContext,
_cachedPage ?? getPageFromPixels(pixels, viewportDimension));
}
@override
void restoreScrollOffset() {
if (!hasPixels) {
final double? value = PageStorage.maybeOf(context.storageContext)
?.readState(context.storageContext) as double?;
if (value != null) {
_pageToUseOnStartup = value;
}
}
}
@override
void saveOffset() {
context.saveOffset(
_cachedPage ?? getPageFromPixels(pixels, viewportDimension));
}
@override
void restoreOffset(double offset, {bool initialRestore = false}) {
if (initialRestore) {
_pageToUseOnStartup = offset;
} else {
jumpTo(getPixelsFromPage(offset));
}
}
@override
bool applyViewportDimension(double viewportDimension) {
final double? oldViewportDimensions =
hasViewportDimension ? this.viewportDimension : null;
if (viewportDimension == oldViewportDimensions) {
return true;
}
final bool result = super.applyViewportDimension(viewportDimension);
final double? oldPixels = hasPixels ? pixels : null;
double page;
if (oldPixels == null) {
page = _pageToUseOnStartup;
} else if (oldViewportDimensions == 0.0) {
page = _cachedPage!;
} else {
page = getPageFromPixels(oldPixels, oldViewportDimensions!);
}
final double newPixels = getPixelsFromPage(page);
_cachedPage = (viewportDimension == 0.0) ? page : null;
if (newPixels != oldPixels) {
correctPixels(newPixels);
return false;
}
return result;
}
@override
void absorb(ScrollPosition other) {
super.absorb(other);
assert(_cachedPage == null);
if (other is! _PagePosition) {
return;
}
if (other._cachedPage != null) {
_cachedPage = other._cachedPage;
}
}
@override
bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {
final double newMinScrollExtent = minScrollExtent + _initialPageOffset;
return super.applyContentDimensions(
newMinScrollExtent,
math.max(newMinScrollExtent, maxScrollExtent - _initialPageOffset),
);
}
@override
PageMetrics copyWith({
double? minScrollExtent,
double? maxScrollExtent,
double? pixels,
double? viewportDimension,
AxisDirection? axisDirection,
double? viewportFraction,
double? devicePixelRatio,
}) {
return PageMetrics(
minScrollExtent: minScrollExtent ??
(hasContentDimensions ? this.minScrollExtent : null),
maxScrollExtent: maxScrollExtent ??
(hasContentDimensions ? this.maxScrollExtent : null),
pixels: pixels ?? (hasPixels ? this.pixels : null),
viewportDimension: viewportDimension ??
(hasViewportDimension ? this.viewportDimension : null),
axisDirection: axisDirection ?? this.axisDirection,
viewportFraction: viewportFraction ?? this.viewportFraction,
devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
);
}
}
class _ForceImplicitScrollPhysics extends ScrollPhysics {
const _ForceImplicitScrollPhysics({
required this.allowImplicitScrolling,
super.parent,
});
@override
_ForceImplicitScrollPhysics applyTo(ScrollPhysics? ancestor) {
return _ForceImplicitScrollPhysics(
allowImplicitScrolling: allowImplicitScrolling,
parent: buildParent(ancestor),
);
}
@override
final bool allowImplicitScrolling;
}
class PageScrollPhysics extends ScrollPhysics {
const PageScrollPhysics({super.parent});
@override
PageScrollPhysics applyTo(ScrollPhysics? ancestor) {
return PageScrollPhysics(parent: buildParent(ancestor));
}
double _getPage(ScrollMetrics position) {
if (position is _PagePosition) {
return position.page!;
}
return position.pixels / position.viewportDimension;
}
double _getPixels(ScrollMetrics position, double page) {
if (position is _PagePosition) {
return position.getPixelsFromPage(page);
}
return page * position.viewportDimension;
}
double _getTargetPixels(
ScrollMetrics position, Tolerance tolerance, double velocity) {
double page = _getPage(position);
if (velocity < -tolerance.velocity) {
page -= 0.5;
} else if (velocity > tolerance.velocity) {
page += 0.5;
}
return _getPixels(position, page.roundToDouble());
}
@override
Simulation? createBallisticSimulation(
ScrollMetrics position, double velocity) {
if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) ||
(velocity >= 0.0 && position.pixels >= position.maxScrollExtent)) {
return super.createBallisticSimulation(position, velocity);
}
final Tolerance tolerance = toleranceFor(position);
final double target = _getTargetPixels(position, tolerance, velocity);
if (target != position.pixels) {
return ScrollSpringSimulation(spring, position.pixels, target, velocity,
tolerance: tolerance);
}
return null;
}
@override
bool get allowImplicitScrolling => false;
}
final CustomPageController _defaultCustomPageController =
CustomPageController();
const PageScrollPhysics _kPagePhysics = PageScrollPhysics();
class CustomPageView extends StatefulWidget {
CustomPageView({
super.key,
this.scrollDirection = Axis.horizontal,
this.reverse = false,
CustomPageController? controller,
this.physics,
this.pageSnapping = true,
this.onPageChanged,
List<Widget> children = const <Widget>[],
this.dragStartBehavior = DragStartBehavior.start,
this.allowImplicitScrolling = false,
this.restorationId,
this.clipBehavior = Clip.hardEdge,
this.scrollBehavior,
this.padEnds = true,
}) : controller = controller ?? _defaultCustomPageController,
childrenDelegate = SliverChildListDelegate(children);
CustomPageView.builder({
super.key,
this.scrollDirection = Axis.horizontal,
this.reverse = false,
CustomPageController? controller,
this.physics,
this.pageSnapping = true,
this.onPageChanged,
required NullableIndexedWidgetBuilder itemBuilder,
ChildIndexGetter? findChildIndexCallback,
int? itemCount,
this.dragStartBehavior = DragStartBehavior.start,
this.allowImplicitScrolling = false,
this.restorationId,
this.clipBehavior = Clip.hardEdge,
this.scrollBehavior,
this.padEnds = true,
}) : controller = controller ?? _defaultCustomPageController,
childrenDelegate = SliverChildBuilderDelegate(
itemBuilder,
findChildIndexCallback: findChildIndexCallback,
childCount: itemCount,
);
CustomPageView.custom({
super.key,
this.scrollDirection = Axis.horizontal,
this.reverse = false,
CustomPageController? controller,
this.physics,
this.pageSnapping = true,
this.onPageChanged,
required this.childrenDelegate,
this.dragStartBehavior = DragStartBehavior.start,
this.allowImplicitScrolling = false,
this.restorationId,
this.clipBehavior = Clip.hardEdge,
this.scrollBehavior,
this.padEnds = true,
}) : controller = controller ?? _defaultCustomPageController;
final bool allowImplicitScrolling;
final String? restorationId;
final Axis scrollDirection;
final bool reverse;
final CustomPageController controller;
final ScrollPhysics? physics;
final bool pageSnapping;
final ValueChanged<int>? onPageChanged;
final SliverChildDelegate childrenDelegate;
final DragStartBehavior dragStartBehavior;
final Clip clipBehavior;
final ScrollBehavior? scrollBehavior;
final bool padEnds;
@override
State<CustomPageView> createState() => _CustomPageViewState();
}
class _CustomPageViewState extends State<CustomPageView> {
int _lastReportedPage = 0;
@override
void initState() {
super.initState();
_lastReportedPage = widget.controller.initialPage;
}
AxisDirection _getDirection(BuildContext context) {
switch (widget.scrollDirection) {
case Axis.horizontal:
assert(debugCheckHasDirectionality(context));
final TextDirection textDirection = Directionality.of(context);
final AxisDirection axisDirection =
textDirectionToAxisDirection(textDirection);
return widget.reverse
? flipAxisDirection(axisDirection)
: axisDirection;
case Axis.vertical:
return widget.reverse ? AxisDirection.up : AxisDirection.down;
}
}
int count = 0;
@override
Widget build(BuildContext context) {
final AxisDirection axisDirection = _getDirection(context);
final ScrollPhysics physics = _ForceImplicitScrollPhysics(
allowImplicitScrolling: widget.allowImplicitScrolling,
).applyTo(
widget.pageSnapping
? _kPagePhysics.applyTo(widget.physics ??
widget.scrollBehavior?.getScrollPhysics(context))
: widget.physics ?? widget.scrollBehavior?.getScrollPhysics(context),
);
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
if (notification.depth == 0 &&
widget.onPageChanged != null &&
notification is ScrollUpdateNotification) {
final PageMetrics metrics = notification.metrics as PageMetrics;
final int currentPage = metrics.page!.round();
print("dsffafafaf $currentPage");
print("dsffafafaflast $count");
if (currentPage != _lastReportedPage) {
_lastReportedPage = currentPage;
widget.onPageChanged!(currentPage);
} else {
if (currentPage == 0) {
if (notification.metrics.outOfRange == true) {
if (count == 0) {
Navigator.of(context).pop();
count = 0;
}
count++;
}
}
}
}
return false;
},
child: Scrollable(
dragStartBehavior: widget.dragStartBehavior,
axisDirection: axisDirection,
controller: widget.controller,
physics: physics,
restorationId: widget.restorationId,
scrollBehavior: widget.scrollBehavior ??
ScrollConfiguration.of(context).copyWith(scrollbars: false),
viewportBuilder: (BuildContext context, ViewportOffset position) {
return Viewport(
cacheExtent: widget.allowImplicitScrolling ? 1.0 : 0.0,
cacheExtentStyle: CacheExtentStyle.viewport,
axisDirection: axisDirection,
offset: position,
clipBehavior: widget.clipBehavior,
slivers: <Widget>[
SliverFillViewport(
viewportFraction: widget.controller.viewportFraction,
delegate: widget.childrenDelegate,
padEnds: widget.padEnds,
),
],
);
},
),
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
description
.add(EnumProperty<Axis>('scrollDirection', widget.scrollDirection));
description.add(
FlagProperty('reverse', value: widget.reverse, ifTrue: 'reversed'));
description.add(DiagnosticsProperty<CustomPageController>(
'controller', widget.controller,
showName: false));
description.add(DiagnosticsProperty<ScrollPhysics>(
'physics', widget.physics,
showName: false));
description.add(FlagProperty('pageSnapping',
value: widget.pageSnapping, ifFalse: 'snapping disabled'));
description.add(FlagProperty('allowImplicitScrolling',
value: widget.allowImplicitScrolling,
ifTrue: 'allow implicit scrolling'));
}
}