当前位置:   article > 正文

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

flutter 监听 pagecontroller

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

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

        开始尝试使用系统给的api进行监听

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

 

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

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

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

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

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

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

还有OverscrollNotification和UserScrollNotification两种滑动类型没有用到,有其他用到的小伙伴可以告诉我这两个回调可以用在什么场景下。

 

自定义的PageView全部代码如下:

  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. import 'dart:async';
  5. import 'dart:math' as math;
  6. import 'package:flutter/cupertino.dart';
  7. import 'package:flutter/rendering.dart';
  8. import 'package:flutter/gestures.dart' show DragStartBehavior;
  9. import 'package:flutter/foundation.dart' show precisionErrorTolerance;
  10. ///自定义的PageView
  11. ///新增了页面加载开始和结束的回调
  12. final PageController _defaultPageController = PageController();
  13. const PageScrollPhysics _kPagePhysics = PageScrollPhysics();
  14. class CustomPageView extends StatefulWidget {
  15. /// Creates a scrollable list that works page by page from an explicit [List]
  16. /// of widgets.
  17. ///
  18. /// This constructor is appropriate for page views with a small number of
  19. /// children because constructing the [List] requires doing work for every
  20. /// child that could possibly be displayed in the page view, instead of just
  21. /// those children that are actually visible.
  22. CustomPageView({
  23. Key key,
  24. this.scrollDirection = Axis.horizontal,
  25. this.reverse = false,
  26. PageController controller,
  27. this.physics,
  28. this.pageSnapping = true,
  29. this.onPageChanged,
  30. this.onPageEndChanged,
  31. this.onPageStartChanged,
  32. List<Widget> children = const <Widget>[],
  33. this.dragStartBehavior = DragStartBehavior.start,
  34. }) : controller = controller ?? _defaultPageController,
  35. childrenDelegate = SliverChildListDelegate(children),
  36. super(key: key);
  37. /// Creates a scrollable list that works page by page using widgets that are
  38. /// created on demand.
  39. ///
  40. /// This constructor is appropriate for page views with a large (or infinite)
  41. /// number of children because the builder is called only for those children
  42. /// that are actually visible.
  43. ///
  44. /// Providing a non-null [itemCount] lets the [CustomPageView] compute the maximum
  45. /// scroll extent.
  46. ///
  47. /// [itemBuilder] will be called only with indices greater than or equal to
  48. /// zero and less than [itemCount].
  49. ///
  50. /// [CustomPageView.builder] by default does not support child reordering. If
  51. /// you are planning to change child order at a later time, consider using
  52. /// [CustomPageView] or [CustomPageView.custom].
  53. CustomPageView.builder({
  54. Key key,
  55. this.scrollDirection = Axis.horizontal,
  56. this.reverse = false,
  57. PageController controller,
  58. this.physics,
  59. this.pageSnapping = true,
  60. this.onPageChanged,
  61. this.onPageEndChanged,
  62. this.onPageStartChanged,
  63. @required IndexedWidgetBuilder itemBuilder,
  64. int itemCount,
  65. this.dragStartBehavior = DragStartBehavior.start,
  66. }) : controller = controller ?? _defaultPageController,
  67. childrenDelegate =
  68. SliverChildBuilderDelegate(itemBuilder, childCount: itemCount),
  69. super(key: key);
  70. CustomPageView.custom({
  71. Key key,
  72. this.scrollDirection = Axis.horizontal,
  73. this.reverse = false,
  74. PageController controller,
  75. this.physics,
  76. this.pageSnapping = true,
  77. this.onPageChanged,
  78. this.onPageEndChanged,
  79. this.onPageStartChanged,
  80. @required this.childrenDelegate,
  81. this.dragStartBehavior = DragStartBehavior.start,
  82. }) : assert(childrenDelegate != null),
  83. controller = controller ?? _defaultPageController,
  84. super(key: key);
  85. /// The axis along which the page view scrolls.
  86. ///
  87. /// Defaults to [Axis.horizontal].
  88. final Axis scrollDirection;
  89. /// Whether the page view scrolls in the reading direction.
  90. ///
  91. /// For example, if the reading direction is left-to-right and
  92. /// [scrollDirection] is [Axis.horizontal], then the page view scrolls from
  93. /// left to right when [reverse] is false and from right to left when
  94. /// [reverse] is true.
  95. ///
  96. /// Similarly, if [scrollDirection] is [Axis.vertical], then the page view
  97. /// scrolls from top to bottom when [reverse] is false and from bottom to top
  98. /// when [reverse] is true.
  99. ///
  100. /// Defaults to false.
  101. final bool reverse;
  102. /// An object that can be used to control the position to which this page
  103. /// view is scrolled.
  104. final PageController controller;
  105. /// How the page view should respond to user input.
  106. ///
  107. /// For example, determines how the page view continues to animate after the
  108. /// user stops dragging the page view.
  109. ///
  110. /// The physics are modified to snap to page boundaries using
  111. /// [PageScrollPhysics] prior to being used.
  112. ///
  113. /// Defaults to matching platform conventions.
  114. final ScrollPhysics physics;
  115. /// Set to false to disable page snapping, useful for custom scroll behavior.
  116. final bool pageSnapping;
  117. /// Called whenever the page in the center of the viewport changes.
  118. final ValueChanged<int> onPageChanged;
  119. final ValueChanged<int> onPageEndChanged;
  120. final ValueChanged<int> onPageStartChanged;
  121. /// A delegate that provides the children for the [CustomPageView].
  122. ///
  123. /// The [CustomPageView.custom] constructor lets you specify this delegate
  124. /// explicitly. The [CustomPageView] and [CustomPageView.builder] constructors create a
  125. /// [childrenDelegate] that wraps the given [List] and [IndexedWidgetBuilder],
  126. /// respectively.
  127. final SliverChildDelegate childrenDelegate;
  128. /// {@macro flutter.widgets.scrollable.dragStartBehavior}
  129. final DragStartBehavior dragStartBehavior;
  130. @override
  131. _CustomPageViewState createState() => _CustomPageViewState();
  132. }
  133. class _CustomPageViewState extends State<CustomPageView> {
  134. int _lastReportedPage = 0;
  135. int _currentReportedPage = 0;
  136. @override
  137. void initState() {
  138. super.initState();
  139. _lastReportedPage = widget.controller.initialPage;
  140. }
  141. AxisDirection _getDirection(BuildContext context) {
  142. switch (widget.scrollDirection) {
  143. case Axis.horizontal:
  144. assert(debugCheckHasDirectionality(context));
  145. final TextDirection textDirection = Directionality.of(context);
  146. final AxisDirection axisDirection =
  147. textDirectionToAxisDirection(textDirection);
  148. return widget.reverse
  149. ? flipAxisDirection(axisDirection)
  150. : axisDirection;
  151. case Axis.vertical:
  152. return widget.reverse ? AxisDirection.up : AxisDirection.down;
  153. }
  154. return null;
  155. }
  156. @override
  157. Widget build(BuildContext context) {
  158. final AxisDirection axisDirection = _getDirection(context);
  159. final ScrollPhysics physics = widget.pageSnapping
  160. ? _kPagePhysics.applyTo(widget.physics)
  161. : widget.physics;
  162. return NotificationListener<ScrollNotification>(
  163. onNotification: (ScrollNotification notification) {
  164. ///滑动开始
  165. if (notification is ScrollStartNotification) {
  166. if (widget.onPageStartChanged != null) {
  167. widget.onPageStartChanged(_currentReportedPage);
  168. }
  169. //print('返回的开始的页码=' + _currentReportedPage.toString());
  170. }
  171. ///滑动中
  172. if (notification.depth == 0 &&
  173. notification is ScrollUpdateNotification) {
  174. final PageMetrics metrics = notification.metrics;
  175. final int currentPage = metrics.page.round();
  176. _currentReportedPage = currentPage;
  177. //print('外部返回的页码=' + currentPage.toString());
  178. //print('外部返回的pixels=' + metrics.pixels.toString());
  179. if (currentPage != _lastReportedPage) {
  180. _lastReportedPage = currentPage;
  181. if (widget.onPageChanged != null) {
  182. widget.onPageChanged(currentPage);
  183. }
  184. //print('返回的页码=' + currentPage.toString());
  185. }
  186. }
  187. ///滑动结束
  188. if (notification.depth == 0 && notification is ScrollEndNotification) {
  189. if (widget.onPageEndChanged != null) {
  190. widget.onPageEndChanged(_currentReportedPage);
  191. }
  192. print('返回的结束的页码=' + _currentReportedPage.toString());
  193. }
  194. return false;
  195. },
  196. child: Scrollable(
  197. dragStartBehavior: widget.dragStartBehavior,
  198. axisDirection: axisDirection,
  199. controller: widget.controller,
  200. physics: physics,
  201. viewportBuilder: (BuildContext context, ViewportOffset position) {
  202. return Viewport(
  203. cacheExtent: 0.0,
  204. axisDirection: axisDirection,
  205. offset: position,
  206. slivers: <Widget>[
  207. SliverFillViewport(
  208. viewportFraction: widget.controller.viewportFraction,
  209. delegate: widget.childrenDelegate,
  210. ),
  211. ],
  212. );
  213. },
  214. ),
  215. );
  216. }
  217. @override
  218. void debugFillProperties(DiagnosticPropertiesBuilder description) {
  219. super.debugFillProperties(description);
  220. description
  221. .add(EnumProperty<Axis>('scrollDirection', widget.scrollDirection));
  222. description.add(
  223. FlagProperty('reverse', value: widget.reverse, ifTrue: 'reversed'));
  224. description.add(DiagnosticsProperty<PageController>(
  225. 'controller', widget.controller,
  226. showName: false));
  227. description.add(DiagnosticsProperty<ScrollPhysics>(
  228. 'physics', widget.physics,
  229. showName: false));
  230. description.add(FlagProperty('pageSnapping',
  231. value: widget.pageSnapping, ifFalse: 'snapping disabled'));
  232. }
  233. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/257406
推荐阅读
相关标签
  

闽ICP备14008679号