[Flutter]從零開始實現一個嵌套滑動的PageView(一)

前言

首先呢,爲何會有PageView嵌套PageView這個需求……web

咱們來看下抖音的交互:markdown

抖音交互
抖音交互

從圖上不難分析出,首頁裏面放了2個tab,右邊的菜單欄則是獨立存在的一個頁面app

再加上嵌套滑動,因此實現方式就是,PageView裏面再嵌套一個佈局,首頁那塊沒法就是在這個嵌套佈局中加入一個TabBarView就行了嘛,菜單欄用狀態管理來更新菜單內容,so easy~ide

然而事實證實我仍是太年輕了……TabBarView其實就是PageView的拓展實現,然鵝,PageView是不支持嵌套滑動的……佈局

解決方案

  1. 手勢監聽,給全部View加上動畫,當劃出菜單的時候,用動畫方式移動底部的標籤欄學習

  2. NestedScrollerView 加上PageView 的physics?動畫

  3. 實現一個能支持嵌套滑動的PageViewui

固然,若是我採用第一種方案也就沒這篇文章了,其中方案調研的血淚史不提也罷。this

首先須要瞭解的知識(課前準備)

  1. Flutter中,提到嵌套滑動,天然第一想到的就是NestedScrollView,因此若是想給PageView加上嵌套滑動機制,學習下NestedScrollView及其核心原理能給咱們很大的幫助。spa

  2. Scrollable.dart 中的ScrollerController,ScrollerPosition等,瞭解其用途、基本含義和使用方法(固然,這篇文章須要的沒那麼多,要想理解,只須要看下ScrollerController、ScrollerPosition便可,那些beginActivity、ScrollActivity什麼的不明白也罷,這篇文章用不到,用ScrollPositionWithSingleContext複製黏貼過來的就行)。

萬里長征第一步,讓它嵌套滑動起來

作好課前準備以後,咱們最簡單的描述一下 NestedScrollView 的嵌套滑動步驟:

  1. 建立_NestedScrollCoordinator ,同時建立2個ScrollController,用於管理總體的CunstomScrollerView和內部primary的可滑動佈局

  2. 經過 自定義的ScrollController ,返回自定義ScrollPosition,順便將Delegate 具體實現交給 _NestedScrollCoordinator來實現。

  3. delegate 經過具體的 applyUserOffset 方法來控制整個列表的內容,順序是往上或者往左滑先滑外部,往下或往右滑先處理內部。

若是實現PageView的嵌套滑動,也能夠採起這個思路。

一些小小坑

  1. PageView自己是不支持primary的,因此若是想像NestedScrollerView那樣不須要給child傳入特定controller,直接用便可的話,就須要實現一個支持Primary的PageView;

  2. PageView是不會保活的,因此若是拉到主PageView的第二頁,包含子PageView的第一頁就會dispose,所以丟失滑動狀態,再拉回來的時候天然展現的是第一頁,而不是嵌套滑動以後的最大頁。因此這塊須要保活處理一下下

核心代碼

 1class _ChildPagePosition extends ScrollPosition
2    implements PageMetricsScrollActivityDelegate 
{
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}
複製代碼

Look Look 效果

NestedPageView 1.0
NestedPageView 1.0

後記

固然,支持嵌套滑動僅僅只是開始……

如圖所示,目前僅僅嵌套滑動了而已,鬆開手以後的physics效果,拉到一半再拉回去等操做也沒特殊處理,嘛,不過這是之後的事了……

相關文章
相關標籤/搜索