[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效果,拉到一半再拉回去等操做也没特殊处理,嘛,不过这是之后的事了……

相关文章
相关标签/搜索