美文网首页
Flutter中PageView的滑动开始监听、完成监听自定义

Flutter中PageView的滑动开始监听、完成监听自定义

作者: 凶许不吃鱼 | 来源:发表于2020-11-23 15:03 被阅读0次

            最近Flutter项目开发中,用到了PageView的上下滚动,但是正常的PageView的使用,onPageChanged的回调,在页面滑到中间的时候,就会把下一个页面的pageIndex值传递过来。但是实际需求中,我需要知道什么时候页面滑动结束,这时候才去执行页面完全展示的方法。所以就需要对PageView进行自定义改造。

    一、尝试了使用系统自带的PageController进行滑动监听,失败

    PageController监听

            结果发现,返回的offset和page是Double类型的,更坑爹的是,在某些手机或者滑动距离过长时,返回的数据不够精准,没办法准确判断是否滑动完成。一看不能用,没办法在系统的api里面找到解决办法,就只能看源码了

    二、查看PageView的源码,自定义pageView

    没办法,只能查看系统源码,看看为什么会出现onPageChanged在中间的时候就返回回调了。在查看源码过程中,发现以下神奇的地方

    系统pageview的build源码

    根据源码我们能看到,PageView在实际上也是通过NotificationListener进行的监听,而且在onPageChanged的返回前,判断了notification is ScrollUpdateNotification。

    ScrollUpdateNotification是什么呢?这就需要我们看一下NotificationListener的回调监听了,也就是返回的ScrollNotification这个类。

    ScrollUpdateNotification继承ScrollNotification

    点击进去看到ScrollUpdateNotification继承ScrollNotification,那是不是还有其他的监听回调类型,可以实现我们的需求呢?果然最后源码中发现,还有ScrollStartNotification、OverscrollNotification、ScrollEndNotification和UserScrollNotification四种滑动回调类型!

    既然我们想要的是滑动完成的回调,就只需要在NotificationListener判断ScrollEndNotification这个滑动类型就行了。

    新增的滑动监听

    全部代码如下:

    ```

    // 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 CustomPageViewextends 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 children =const [],

        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 AxisscrollDirection;

      /// 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 boolreverse;

      /// An object that can be used to control the position to which this page

    /// view is scrolled.

      final PageControllercontroller;

      /// 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 ScrollPhysicsphysics;

      /// Set to false to disable page snapping, useful for custom scroll behavior.

      final boolpageSnapping;

      /// Called whenever the page in the center of the viewport changes.

      final ValueChangedonPageChanged;

      final ValueChangedonPageEndChanged;

      final ValueChangedonPageStartChanged;

      /// 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 SliverChildDelegatechildrenDelegate;

      /// {@macro flutter.widgets.scrollable.dragStartBehavior}

      final DragStartBehaviordragStartBehavior;

      @override

      _CustomPageViewStatecreateState() =>_CustomPageViewState();

    }

    class _CustomPageViewStateextends State {

    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

      Widgetbuild(BuildContext context) {

    final AxisDirection axisDirection = _getDirection(context);

        final ScrollPhysics physics =widget.pageSnapping

            ? _kPagePhysics.applyTo(widget.physics)

    :widget.physics;

        return NotificationListener(

    onNotification: (ScrollNotification notification) {

    ///滑动开始

            if (notificationis ScrollStartNotification) {

    if (widget.onPageStartChanged !=null) {

    widget.onPageStartChanged(_currentReportedPage);

              }

    //print('返回的开始的页码=' + _currentReportedPage.toString());

            }

    ///滑动中

            if (notification.depth ==0 &&

    notificationis 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 && notificationis 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: [

    SliverFillViewport(

    viewportFraction:widget.controller.viewportFraction,

                    delegate:widget.childrenDelegate,

                  ),

                ],

              );

            },

          ),

        );

      }

    @override

      void debugFillProperties(DiagnosticPropertiesBuilder description) {

    super.debugFillProperties(description);

        description

    .add(EnumProperty('scrollDirection', widget.scrollDirection));

        description.add(

    FlagProperty('reverse', value:widget.reverse, ifTrue:'reversed'));

        description.add(DiagnosticsProperty(

    'controller', widget.controller,

            showName:false));

        description.add(DiagnosticsProperty(

    'physics', widget.physics,

            showName:false));

        description.add(FlagProperty('pageSnapping',

            value:widget.pageSnapping, ifFalse:'snapping disabled'));

      }

    }

    ```

    相关文章

      网友评论

          本文标题:Flutter中PageView的滑动开始监听、完成监听自定义

          本文链接:https://www.haomeiwen.com/subject/jfxviktx.html