赞
踩
最近Flutter项目开发中,用到了PageView的上下滚动,但是正常的PageView的使用,onPageChanged的回调,在页面滑到中间的时候,就会把下一个页面的pageIndex值传递过来。但是实际需求中,我需要知道什么时候页面滑动结束,这时候才去执行页面完全展示的方法。所以就需要对PageView进行自定义改造。
开始尝试使用系统给的api进行监听
结果发现,返回的offset和page是Double类型的,更坑爹的是,在某些手机或者滑动距离过长时,返回的数据不够精准,没办法准确判断是否滑动完成。一看不能用,没办法在系统的api里面找到解决办法,就只能看源码了
查看系统源码,看看为什么会出现onPageChanged在中间的时候就返回回调了。在查看源码过程中,发现以下神奇的地方:
根据源码我们能看到,PageView在实际上也是通过NotificationListener进行的监听,而且在onPageChanged的返回前,判断了notification is ScrollUpdateNotification。
ScrollUpdateNotification是什么呢?这就需要我们看一下NotificationListener的回调监听了,也就是返回的ScrollNotification这个类。
点击进去看到ScrollUpdateNotification继承ScrollNotification,那是不是还有其他的监听回调类型,可以实现我们的需求呢?果然最后源码中发现,还有ScrollStartNotification、OverscrollNotification、ScrollEndNotification和UserScrollNotification四种滑动回调类型!
既然我们想要的是滑动完成的回调,就只需要在NotificationListener判断ScrollEndNotification这个滑动类型就行了。
还有OverscrollNotification和UserScrollNotification两种滑动类型没有用到,有其他用到的小伙伴可以告诉我这两个回调可以用在什么场景下。
自定义的PageView全部代码如下:
- // Copyright 2016 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- import 'dart:async';
- import 'dart:math' as math;
-
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/rendering.dart';
- import 'package:flutter/gestures.dart' show DragStartBehavior;
- import 'package:flutter/foundation.dart' show precisionErrorTolerance;
-
- ///自定义的PageView
- ///新增了页面加载开始和结束的回调
-
- final PageController _defaultPageController = PageController();
- const PageScrollPhysics _kPagePhysics = PageScrollPhysics();
-
- class CustomPageView extends StatefulWidget {
- /// Creates a scrollable list that works page by page from an explicit [List]
- /// of widgets.
- ///
- /// This constructor is appropriate for page views with a small number of
- /// children because constructing the [List] requires doing work for every
- /// child that could possibly be displayed in the page view, instead of just
- /// those children that are actually visible.
- CustomPageView({
- Key key,
- this.scrollDirection = Axis.horizontal,
- this.reverse = false,
- PageController controller,
- this.physics,
- this.pageSnapping = true,
- this.onPageChanged,
- this.onPageEndChanged,
- this.onPageStartChanged,
- List<Widget> children = const <Widget>[],
- this.dragStartBehavior = DragStartBehavior.start,
- }) : controller = controller ?? _defaultPageController,
- childrenDelegate = SliverChildListDelegate(children),
- super(key: key);
-
- /// 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 [CustomPageView] compute the maximum
- /// scroll extent.
- ///
- /// [itemBuilder] will be called only with indices greater than or equal to
- /// zero and less than [itemCount].
- ///
- /// [CustomPageView.builder] by default does not support child reordering. If
- /// you are planning to change child order at a later time, consider using
- /// [CustomPageView] or [CustomPageView.custom].
- CustomPageView.builder({
- Key key,
- this.scrollDirection = Axis.horizontal,
- this.reverse = false,
- PageController controller,
- this.physics,
- this.pageSnapping = true,
- this.onPageChanged,
- this.onPageEndChanged,
- this.onPageStartChanged,
- @required IndexedWidgetBuilder itemBuilder,
- int itemCount,
- this.dragStartBehavior = DragStartBehavior.start,
- }) : controller = controller ?? _defaultPageController,
- childrenDelegate =
- SliverChildBuilderDelegate(itemBuilder, childCount: itemCount),
- super(key: key);
-
- CustomPageView.custom({
- Key key,
- this.scrollDirection = Axis.horizontal,
- this.reverse = false,
- PageController controller,
- this.physics,
- this.pageSnapping = true,
- this.onPageChanged,
- this.onPageEndChanged,
- this.onPageStartChanged,
- @required this.childrenDelegate,
- this.dragStartBehavior = DragStartBehavior.start,
- }) : assert(childrenDelegate != null),
- controller = controller ?? _defaultPageController,
- super(key: key);
-
- /// The axis along which the page view scrolls.
- ///
- /// Defaults to [Axis.horizontal].
- final Axis scrollDirection;
-
- /// Whether the page view scrolls in the reading direction.
- ///
- /// For example, if the reading direction is left-to-right and
- /// [scrollDirection] is [Axis.horizontal], then the page view scrolls from
- /// left to right when [reverse] is false and from right to left when
- /// [reverse] is true.
- ///
- /// Similarly, if [scrollDirection] is [Axis.vertical], then the page view
- /// scrolls from top to bottom when [reverse] is false and from bottom to top
- /// when [reverse] is true.
- ///
- /// Defaults to false.
- final bool reverse;
-
- /// An object that can be used to control the position to which this page
- /// view is scrolled.
- final PageController controller;
-
- /// How the page view should respond to user input.
- ///
- /// For example, determines how the page view continues to animate after the
- /// user stops dragging the page view.
- ///
- /// The physics are modified to snap to page boundaries using
- /// [PageScrollPhysics] prior to being used.
- ///
- /// Defaults to matching platform conventions.
- final ScrollPhysics physics;
-
- /// Set to false to disable page snapping, useful for custom scroll behavior.
- final bool pageSnapping;
-
- /// Called whenever the page in the center of the viewport changes.
- final ValueChanged<int> onPageChanged;
- final ValueChanged<int> onPageEndChanged;
- final ValueChanged<int> onPageStartChanged;
-
- /// A delegate that provides the children for the [CustomPageView].
- ///
- /// The [CustomPageView.custom] constructor lets you specify this delegate
- /// explicitly. The [CustomPageView] and [CustomPageView.builder] constructors create a
- /// [childrenDelegate] that wraps the given [List] and [IndexedWidgetBuilder],
- /// respectively.
- final SliverChildDelegate childrenDelegate;
-
- /// {@macro flutter.widgets.scrollable.dragStartBehavior}
- final DragStartBehavior dragStartBehavior;
-
- @override
- _CustomPageViewState createState() => _CustomPageViewState();
- }
-
- class _CustomPageViewState extends State<CustomPageView> {
- int _lastReportedPage = 0;
- int _currentReportedPage = 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;
- }
- return null;
- }
-
- @override
- Widget build(BuildContext context) {
- final AxisDirection axisDirection = _getDirection(context);
- final ScrollPhysics physics = widget.pageSnapping
- ? _kPagePhysics.applyTo(widget.physics)
- : widget.physics;
-
- return NotificationListener<ScrollNotification>(
- onNotification: (ScrollNotification notification) {
- ///滑动开始
- if (notification is ScrollStartNotification) {
- if (widget.onPageStartChanged != null) {
- widget.onPageStartChanged(_currentReportedPage);
- }
- //print('返回的开始的页码=' + _currentReportedPage.toString());
- }
-
- ///滑动中
- if (notification.depth == 0 &&
- notification is ScrollUpdateNotification) {
- final PageMetrics metrics = notification.metrics;
- final int currentPage = metrics.page.round();
- _currentReportedPage = currentPage;
- //print('外部返回的页码=' + currentPage.toString());
- //print('外部返回的pixels=' + metrics.pixels.toString());
- if (currentPage != _lastReportedPage) {
- _lastReportedPage = currentPage;
- if (widget.onPageChanged != null) {
- widget.onPageChanged(currentPage);
- }
- //print('返回的页码=' + currentPage.toString());
- }
- }
-
- ///滑动结束
- if (notification.depth == 0 && notification is ScrollEndNotification) {
- if (widget.onPageEndChanged != null) {
- widget.onPageEndChanged(_currentReportedPage);
- }
- print('返回的结束的页码=' + _currentReportedPage.toString());
- }
- return false;
- },
- child: Scrollable(
- dragStartBehavior: widget.dragStartBehavior,
- axisDirection: axisDirection,
- controller: widget.controller,
- physics: physics,
- viewportBuilder: (BuildContext context, ViewportOffset position) {
- return Viewport(
- cacheExtent: 0.0,
- axisDirection: axisDirection,
- offset: position,
- slivers: <Widget>[
- SliverFillViewport(
- viewportFraction: widget.controller.viewportFraction,
- delegate: widget.childrenDelegate,
- ),
- ],
- );
- },
- ),
- );
- }
-
- @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<PageController>(
- 'controller', widget.controller,
- showName: false));
- description.add(DiagnosticsProperty<ScrollPhysics>(
- 'physics', widget.physics,
- showName: false));
- description.add(FlagProperty('pageSnapping',
- value: widget.pageSnapping, ifFalse: 'snapping disabled'));
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。