首先呢,爲何會有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效果,拉到一半再拉回去等操做也沒特殊處理,嘛,不過這是之後的事了……