深入进阶-一张图理清Flutter的滑动原理

4,639 阅读4分钟

深入进阶-一张图理清Flutter的滑动原理

导语

一次需求中遇到了这样的场景,PageView中有三个页面,其中一个页面是TabBarView结构。结果出现了当滑动到TabBar的时候,外层PageView无法滑动(滑动冲突)。最终在stackoverflow上找到了这个问题的解法,过程中顺便将Flutter的手势与滑动机制总结了一番。这也是Flutter进阶必须掌握的一个知识点,相信我,这一定是全网最详细,易懂的总结!!

这个系列会分为三篇:

1、从一次点击探寻Flutter的事件分发原理

2、一张图理清Flutter的滑动原理

3、实战Flutter滑动原理

4、解决Flutter滑动冲突的两种思路

**读完本文你将收获:PageView的嵌套结构,以及Flutter滑动核心原理 **


引言

上一篇的文章中,我们以一次点击事件为例学习了Flutter的事件分发过程。这次,我们通过对PageView拆解,从整体上认识Scrollable是如何实现滑动的。

1、基本组件认识

在Flutter的滑动体系中有几个原子组件需要了解,滑动功能正是由这些原子组件组合而成。

Viewport 视窗

  • 视窗,一个固定大小的窗口,内部有一个更大的部件
  • [Viewport]是滚动控件的关键。它根据子元素的大小和给定的[ViewportOffset offset]显示不同的子元素(这里的offset一指ScrollPosition)。 随着偏移量的变化,通过[Viewport]发起通知,渲染不同的子元素。

ScrollPosition extend ViewportOffset 滑动位置(控制器)

  • 确定在[Viewport]哪一部分显示。
  • [pixels]值决定了滚动视图用来选择显示其内容的哪一部分的滚动偏移量。当用户滚动视图时,该值会发生变化,从而改变所显示的内容。
  • [ScrollPosition]将[physics]应用于滚动,并存储 [minScrollExtent]和[maxScrollExtent]。
  • 每个Scrollable对应唯一的ScrollPosition管理滑动信息
  • 这个对象是一个[Listenable],当[pixels] 发生变化时通知它的观察者。
  • 随着时间的推移,一个[Scrollable]可能有许多不同的[ScrollPosition] ///对象。例如,[Scrollable.physics]发生了改变,[Scrollable]创建一个新的[ScrollPosition]。需要将原来postion的状态迁移到新的position中,子类实现[absorb]。
  • keepScrollOffset用[PageStorage]保存当前滚动偏移量,如果这个滚动位置的可滚动被重新创建则恢复它。

ScrollController 滑动(位置控制器)

  • 滚动控制器创建一个[ScrollPosition]来管理特定于单个[Scroll] widget的状态。要使用自定义的[ScrollPosition][ScrollController]的子类要覆盖createScrollPosition(解决滑动冲突的一个思路)。
  • 准确的来说,ScrollController并不是实际的滚动控制器,它可以对多个ScrollPosition进行管理,实际的控制滚动行为的对象是Position。在PageView中来自于PageController。

ScrollPhysics 滑动物理模拟

  • 滑动模拟器,将用户的交互处理成更接近真实世界的行为,例如,手指快速滑动之后,产生的惯性滑动效果

总结:这几个概念是滑动的核心知识,简单的来说,Flutter中的滑动可以理解为,ScrollPosition中的偏移量改变发起通知,Viewport接受到通知后根据偏移量渲染内部不同的位置,让用户感受起来觉得列表"滑动了"。那这个ScrollPostion的偏移量是被谁改变的呢,直观的我们能猜想到肯定和手势有关。这不,下面的图完整的描述了Scrollable的结构。

核心图来袭!!!!!

2、PageView核心结构

有了上面的基础认知之后,我们以PageView为例,从整体上把握滑动原理。

这个图可以从几个关键部分入手

(一)PageView

可以看出,PageView就是基于Scrllable进行了定制,通过封装Notification获取到ScrollNotification类的通知,根据通知信息里的偏移判断当前页面是否发生了切换,然后回调onPageChanged

(二)RawGestureDetector

手势收集类,在ScrollablesetCanDrag方法中,绑定了VerticalDragGestureRecognizer或者HorizontalDragGestureRecognizer用来收集垂直或水平方向的滑动信息。

(三)ScrollController与ScrollPosition

ScrollPositionScrollable中实际控制滑动的对象,在ScrollControllerattach方法中。ScrollPosition会将ScrollController作为它的观察者添加到Listeners中,我们往往使用ScrollController.addListener方法添加滚动监听,实际上的通知顺序是:ScrollPosition->ScrollController->添加的回调

(四)Viewport

接受来自ScrollPosition的偏移量,绘制不同的区域完成"滑动"。

3、基本流程分析

当手指在屏幕上滑动额时候,首先RawGestureDetector收集了手势信息(里面处理了手势竞争的流程,下期展开叙述),之后将手势信息回调到Scrollable中。Scrollable接受到信息后,通过ScrollPosition进行滑动控制包含(1)修改偏移量,通知Viewport绘制不同的区域 (2)通知ScrollController,进行观察者的通知。

最后

本期主要从宏观上通过对PageView为例分析了Scrollable的组件结构,拆解学习每Scrollable的每一部分功能。下一期将从手指触摸开始,深入细节分析滑动原理与解决滑动冲突的思路。作图不易求个赞吧QAQ