首先呢,为何会有PageView嵌套PageView这个需求……web
咱们来看下抖音的交互:markdown
从图上不难分析出,首页里面放了2个tab,右边的菜单栏则是独立存在的一个页面app
再加上嵌套滑动,因此实现方式就是,PageView里面再嵌套一个布局,首页那块没法就是在这个嵌套布局中加入一个TabBarView就行了嘛,菜单栏用状态管理来更新菜单内容,so easy~ide
然而事实证实我仍是太年轻了……TabBarView其实就是PageView的拓展实现,然鹅,PageView是不支持嵌套滑动的……布局
手势监听,给全部View加上动画,当划出菜单的时候,用动画方式移动底部的标签栏学习
NestedScrollerView 加上PageView 的physics?动画
实现一个能支持嵌套滑动的PageViewui
固然,若是我采用第一种方案也就没这篇文章了,其中方案调研的血泪史不提也罢。this
Flutter中,提到嵌套滑动,天然第一想到的就是NestedScrollView,因此若是想给PageView加上嵌套滑动机制,学习下NestedScrollView及其核心原理能给咱们很大的帮助。spa
Scrollable.dart 中的ScrollerController,ScrollerPosition等,了解其用途、基本含义和使用方法(固然,这篇文章须要的没那么多,要想理解,只须要看下ScrollerController、ScrollerPosition便可,那些beginActivity、ScrollActivity什么的不明白也罢,这篇文章用不到,用ScrollPositionWithSingleContext复制黏贴过来的就行)。
作好课前准备以后,咱们最简单的描述一下 NestedScrollView 的嵌套滑动步骤:
建立_NestedScrollCoordinator ,同时建立2个ScrollController,用于管理总体的CunstomScrollerView和内部primary的可滑动布局
经过 自定义的ScrollController ,返回自定义ScrollPosition,顺便将Delegate 具体实现交给 _NestedScrollCoordinator来实现。
delegate 经过具体的 applyUserOffset 方法来控制整个列表的内容,顺序是往上或者往左滑先滑外部,往下或往右滑先处理内部。
若是实现PageView的嵌套滑动,也能够采起这个思路。
PageView自己是不支持primary的,因此若是想像NestedScrollerView那样不须要给child传入特定controller,直接用便可的话,就须要实现一个支持Primary的PageView;
PageView是不会保活的,因此若是拉到主PageView的第二页,包含子PageView的第一页就会dispose,所以丢失滑动状态,再拉回来的时候天然展现的是第一页,而不是嵌套滑动以后的最大页。因此这块须要保活处理一下下
1class _ChildPagePosition extends ScrollPosition
2 implements PageMetrics, ScrollActivityDelegate {
3 _ChildPagePosition({
4 this.parentController,
5 ScrollPhysics physics,
6 ScrollContext context,
7 this.initialPage = 0,
8 bool keepPage = true,
9 double viewportFraction = 1.0,
10 double initialPixels = 0.0,
11 ScrollPosition oldPosition,
12 })
13 : assert(initialPage != null),
14 assert(keepPage != null),
15 assert(viewportFraction != null),
16 assert(viewportFraction > 0.0),
17 _viewportFraction = viewportFraction,
18 _pageToUseOnStartup = initialPage.toDouble(),
19 super(
20 physics: physics,
21 context: context,
22 keepScrollOffset: keepPage,
23 oldPosition: oldPosition,
24 ) {
25 // If oldPosition is not null, the superclass will first call absorb(),
26 // which may set _pixels and _activity.
27 if (pixels == null && initialPixels != null)
28 correctPixels(initialPixels);
29 if (activity == null)
30 goIdle();
31 assert(activity != null);
32 }
33
34 /// 中间一大堆无关方法略过
35
36 @override
37 void applyUserOffset(double delta) {
38 updateUserScrollDirection(
39 delta > 0.0 ? ScrollDirection.forward : ScrollDirection.reverse);
40 final double newPixels = pixels -
41 physics.applyPhysicsToUserOffset(this, delta);
42 final double overScroll = physics.applyBoundaryConditions(this, newPixels);
43 if (overScroll == 0) {
44 setPixels(newPixels);
45 } else {
46 if(parentController!=null){
47 if(parentController.position is _PagePosition){
48 (parentController.position as _PagePosition).applyClampedDragUpdate(-overScroll);
49 }
50 }
51 print("触发上级滑动");
52 }
53 }
54}
复制代码
固然,支持嵌套滑动仅仅只是开始……
如图所示,目前仅仅嵌套滑动了而已,松开手以后的physics效果,拉到一半再拉回去等操做也没特殊处理,嘛,不过这是之后的事了……