持续创作,加速生长!这是我参加「日新计划 10 月更文应战」的第31天,点击查看活动概况

当内容超过显现视口(ViewPort)时,假如没有特别处理,Flutter则会提示Overflow过错。为此,Flutter供给了多种可翻滚widget(Scrollable Widget)用于显现列表和长布局。

Flutter中有两种布局模型:

  • 依据 RenderBox 的盒模型布局。
  • 依据 Sliver ( RenderSliver ) 按需加载列表布局。

通常可翻滚组件的子组件可能会十分多、占用的总高度也会十分大;假如要一次性将子组件悉数构建出将会十分贵重!为此,Flutter中提出一个Sliver(中文为“薄片”的意思)概念,Sliver 能够包括一个或多个子组件。Sliver 的首要作用是配合:加载子组件并承认每一个子组件的布局和绘制信息,假如 Sliver 能够包括多个子组件时,通常会实现按需加载模型。

只要当Sliver 出现在视口中时才会去构建它,这种模型也称为“依据Sliver的列表按需加载模型”。可翻滚组件中有许多都支持依据Sliver的按需加载模型,如ListViewGridView,可是也有不支持该模型的,如SingleChildScrollView

Flutter 中的可翻滚首要由三个人物组成:Scrollable、Viewport 和 Sliver:

  • Scrollable :用于处理滑动手势,承认滑动偏移,滑动偏移变化时构建 Viewport 。
  • Viewport:显现的视窗,即列表的可视区域;
  • Sliver:视窗里显现的元素。

具体布局过程:

  1. Scrollable 监听到用户滑动行为后,依据最新的滑动偏移构建 Viewport 。
  2. Viewport 将当时视口信息和装备信息经过 SliverConstraints 传递给 Sliver。
  3. Sliver 中对子组件(RenderBox)按需进行构建和布局,然后承认本身的方位、绘制等信息,保存在 geometry 中(一个 SliverGeometry 类型的目标)

比方有一个 ListView,巨细撑满屏幕,假定它有 100 个列表项(都是RenderBox)且每个列表项高度相同,结构如下:

31、Flutter之 可滚动组件简介

图中白色区域为设备屏幕,也是 Scrollable 、 Viewport 和 Sliver 所占用的空间,三者所占用的空间重合,父子关系为:Sliver 父组件为 Viewport,Viewport的 父组件为 Scrollable 。留意ListView 中只要一个 Sliver,在 Sliver 中实现了子组件的按需加载。

其中顶部和底部灰色的区域为 cacheExtent,它表明预烘托的高度,需求留意这是在可视区域之外,假如 RenderBox 进入这个区域内,即使它还未显现在屏幕上,也是要先进行构建的,预烘托是为了后边进入 Viewport 的时分更丝滑。cacheExtent 的默许值是 250,在构建可翻滚列表时咱们能够指定这个值,这个值最终会传给 Viewport。

Scrollable

用于处理滑动手势,承认滑动偏移,滑动偏移变化时构建 Viewport,咱们看一下其要害的特点:

Scrollable({
  ...
  this.axisDirection = AxisDirection.down,
  this.controller,
  this.physics,
  required this.viewportBuilder, //后边介绍
})
  • axisDirection 翻滚方向。
  • physics:此特点承受一个ScrollPhysics类型的目标,它决议可翻滚组件怎么响应用户操作,比方用户滑动完抬起手指后,持续执行动画;或者滑动到边界时,怎么显现。默许情况下,Flutter会依据具体渠道分别运用不同的ScrollPhysics目标,应用不同的显现作用,如当滑动到边界时,持续拖动的话,在 iOS 上会出现弹性作用,而在 Android 上会出现微光作用。假如你想在一切渠道下运用同一种作用,能够显式指定一个固定的ScrollPhysics,Flutter SDK中包括了两个ScrollPhysics的子类,他们能够直接运用:
AlwaysScrollableScrollPhysics:总是能够滑动
NeverScrollableScrollPhysics:禁止翻滚
BouncingScrollPhysics :内容超过一屏 上拉有回弹作用
ClampingScrollPhysics :包裹内容 不会有回弹
  • controller:此特点承受一个ScrollController目标。ScrollController的首要作用是操控翻滚方位和监听翻滚事件。默许情况下,Widget树中会有一个默许的PrimaryScrollController,假如子树中的可翻滚组件没有显式的指定controller,并且primary特点值为true时(默许就为true),可翻滚组件会运用这个默许的PrimaryScrollController。这种机制带来的优点是父组件能够操控子树中可翻滚组件的翻滚行为,例如,Scaffold正是运用这种机制在iOS中实现了点击导航栏回到顶部的功用。
  • viewportBuilder:构建 Viewport 的回调。当用户滑动时,Scrollable 会调用此回调构建新的 Viewport,一起传递一个 ViewportOffset 类型的 offset 参数,该参数描绘 Viewport 应该显现那一部分内容。留意从头构建 Viewport 并不是一个贵重的操作,由于 Viewport 本身也是 Widget,仅仅装备信息,Viewport 变化时对应的 RenderViewport 会更新信息,并不会跟着 Widget 进行从头构建。

主轴和纵轴

在可翻滚组件的坐标描绘中,通常将翻滚方向称为主轴,非翻滚方向称为纵轴。由于可翻滚组件的默许方向一般都是沿笔直方向,所以默许情况下主轴就是指笔直方向,水平方向同理。

Viewport

Viewport 比较简单,用于烘托当时视口中需求显现 Sliver。

Viewport({
  Key? key,
  this.axisDirection = AxisDirection.down,
  this.crossAxisDirection,
  this.anchor = 0.0,
  required ViewportOffset offset, // 用户的翻滚偏移
  // 类型为Key,表明从什么地方开端绘制,默许是第一个元素
  this.center,
  this.cacheExtent, // 预烘托区域
  //该参数用于配合解说cacheExtent的含义,也能够为主轴长度的乘数
  this.cacheExtentStyle = CacheExtentStyle.pixel, 
  this.clipBehavior = Clip.hardEdge,
  List<Widget> slivers = const <Widget>[], // 需求显现的 Sliver 列表
})

需求留意的是:

  • offset:该参数为Scrollabel 构建 Viewport 时传入,它描绘了 Viewport 应该显现那一部分内容。
  • cacheExtent 和 cacheExtentStyle:CacheExtentStyle 是一个枚举,有 pixel 和 viewport 两个取值。当 cacheExtentStyle 值为 pixel 时,cacheExtent 的值为预烘托区域的具体像素长度;当值为 viewport 时,cacheExtent 的值是一个乘数,表明有几个 viewport 的长度,最终的预烘托区域的像素长度为:cacheExtent * viewport 的积, 这在每一个列表项都占满整个 Viewport 时比较实用,这时 cacheExtent 的值就表明前后各缓存几个页面。

Sliver

Sliver 首要作用是对子组件进行构建和布局,比方 ListView 的 Sliver 需求实现子组件(列表项)按需加载功用,只要当列表项进入预烘托区域时才会去对它进行构建和布局、烘托。

Sliver 对应的烘托目标类型是 RenderSliver,RenderSliver 和 RenderBox 的相同点是都继承自 RenderObject 类,不同点是在布局的时分束缚信息不同。RenderBox 在布局时父组件传递给它的束缚信息对应的是 BoxConstraints,只包括最大宽高的束缚;而 RenderSliver 在布局时父组件(列表)传递给它的束缚是对应的是 SliverConstraints。

可翻滚组件的通用装备

几乎一切的可翻滚组件在结构时都能指定 scrollDirection(滑动的主轴)、reverse(滑动方向是否反向)、controller、physics 、cacheExtent ,这些特点最终会透传给对应的 Scrollable 和 Viewport,这些特点咱们能够认为是可翻滚组件的通用特点.

reverse表明是否按照阅览方向相反的方向滑动,如:scrollDirection值为Axis.horizontal 时,即滑动发现为水平,假如阅览方向是从左到右。reversetrue时,那么滑动方向就是从右往左。

ScrollController

可翻滚组件都有一个 controller 特点,经过该特点咱们能够指定一个 ScrollController 来操控可翻滚组件的翻滚,比方能够经过ScrollController来同步多个组件的滑动联动。

子节点缓存

按需加载子组件在大多数场景中都能有正收益,可是有些时分也会有副作用。比方有一个页面,它由一个ListView 组成,咱们希望在页面顶部显现一块内容, 这部分内容的数据需求在每次页面打开时经过网络来获取,为此咱们通一个 Header 组件来实现,它是一个 StatefulWidget ,会在initState 中恳求网络数据,然后将它作为 ListView 的第一个孩子。现在问题来了,由于 ListView 是按需加载子节点的,这意味着假如 Header 滑出 Viewport 的预烘托区域之外时就会被销毁,从头滑入后又会被从头构建,这样就会发起屡次网络恳求,不符合咱们希望。

为了处理上述问题,可翻滚组件供给了一种缓存子节点的通用处理方案,它允许开发者对特定的子界限进行缓存.

Scrollbar

Scrollbar是一个Material风格的翻滚指示器(翻滚条),假如要给可翻滚组件增加翻滚条,只需将Scrollbar作为可翻滚组件的恣意一个父级组件即可,如:

Scrollbar(
  child: SingleChildScrollView(
    ...
  ),
);

ScrollbarCupertinoScrollbar都是经过监听翻滚通知来承认翻滚条方位的。

CupertinoScrollbar

CupertinoScrollbar是 iOS 风格的翻滚条,假如你运用的是Scrollbar,那么在iOS渠道它会自动切换为CupertinoScrollbar

总结 本篇介绍了可翻滚组件的概念和具体的组成,结构。后续会具体介绍一些可翻滚组件的运用详解。如ListView,GridView等。