滚动条

Catalogue   

分类

  • CustomScrollView
  • DraggableScrollableSheet
  • GridView
  • ListView
  • NestedScrollView
  • NotificationListener
  • PageView
  • RefreshIndicator
  • ReorderableListView
  • ScrollCongiuration:一个InheritedWidget,作用是给Scrollable传递ScrollBehavior
  • Scrollable:
  • Scrollbar:
  • SingleChildScrollView

滑动体系

  • Viewport:视口,一个固定大小的窗口;它根据自元素的大小和给定的[ViewportOffset offset]显示不同的子元素(offset指ScrollPosition)
    • ShrinkWrappingViewport:在performLayout阶段才能确定自己的大小,因为它的大小依赖于自己的子widget,需要先统计子view的大小,才能确定自身的大小
    • Viewport:在performSize阶段就可以确定自己的大小
  • ScrollPosition extend ViewportOffset:滑动位置控制器。
    • 确定在[Viewport]哪一部分显示
    • [pixels]值决定了滚动视图用来选择显示其内容的哪一部分的滚动偏移量。当用户滚动视图时,该值会发生变化,从而改变显示内容
    • [ScrollPosition]将[physics]应用于滚动,并存储[minScrollExtent]和[maxScrollExtent]
    • 每个Scrollable对应唯一的ScrollPosition管理滑动信息
    • keepScrollOffset:用[PageStorage]保存当前滚动偏移量
  • ScrollController:滑动控制器
  • ScrollPhysics:滑动物理模拟
  • ScrollView:可滑动视图的父类,ListView、CustomScrollView、GridView都是它的子类,它们通过实现buildSlivers函数为ScroolView提供子视图,同时将ScrollController、ScrollPhysics、ViewportBuilder、children等传递给Scrollable
  • ScrollActivity:表示滑动过程中某一阶段,记录了当前的状态,比如是否滑动中、当前的滑动速度等
    • delegate,ScrollActivityDelegate,有些更新滑动位置的实现,一般是ScrollPosition寄其子类
    • shouldIgnorePotinter,是否忽略触摸事件,这里的主体,是 Scrollable 的子 widget,也就是 Viewport,而在 Scrollable 中用于接收手势滑动的 RawGestureDetector 在它之上,也就是说,这个参数并不是控制是否检测滑动手势,而是待滑动的内容是否可以接收事件,所以,在众多 ScrollActivity 中,只有 HoldScrollActivity 和 IdleScrollActivity 中它的值才为 true
    • isScrolling:是否处于滑动状态
    • velocity:如果是滑动状态,当前的滑动速度
  • ScrollPhysics:描述滚动的物理属性
    • spring:SpringDescription,描述了滑动的一些物理特性,会在创建Simulation时传递过去
    • tolerance,Tolerance,定义了一些可忽略的距离、速度、时间等
    • flingDistance,定义了最小的可被认定为fling手势的距离
    • flingVelocity,定义了最小的可被认定为fling手势的速度,和最大的fling速度
    • dragStartDistanceMotionThreshold,定义了开始滑动时,可被认定是滑动手势的最小距离
    • allowImplicitScrolling:

ScrollActivity分类

不滑动

  • HoldScrollActivity
  • IdleScrollActivity

滑动

  • DragScrollActivity(事件驱动):滑动过程是根据外部传进来的滑动事件,来决定是否以及如何更新视图
  • BallisticScrollActivity(速度驱动):当 drag 系列事件结束后,会留下一个滑动速度,此时滑动并不会停止,而是在基于这个速度下,做减速滑动,直到速度为 0,或者滑动到边界,这个阶段,对应的就是 BallisticScrollActivity
  • DrivenScrollActivity(动画驱动):当我们直接通过 ScrollController 控制 Scrollable 进行滑动时,一般就是调用 animateTo,会创建一个 DrivenScrollActivity,根据当前给出的 duration、curve 等,创建一个动画并执行。在 BallisticScrollActivity 执行过程中,用于决定滑动位置的就是 Simulation

ScrollPhysics分类

  • BouncingScrollPhysics:允许滚动超出边界,但之后内容会回弹
  • ClampingScrollPhysics:防止滚动超出边界
  • AlwaysScrollableScrollPhysics:始终响应用户的滚动
  • NeverScrollableScrollPhysics:不响应用户的滚动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// [position] 当前的位置, [offset] 用户拖拽距离
/// 将用户拖拽距离 offset 转为需要移动的 pixels
double applyPhysicsToUserOffset(ScrollMetrics position, double offset)

/// 返回 overscroll ,如果返回 0 ,overscroll 就一直是0
/// 返回边界条件
double applyBoundaryConditions(ScrollMetrics position, double value)

///创建一个滚动的模拟器
Simulation createBallisticSimulation(ScrollMetrics position, double velocity)

///最小滚动数据
double get minFlingVelocity

///传输动量,返回重复滚动时的速度
double carriedMomentum(double existingVelocity)

///最小的开始拖拽距离
double get dragStartDistanceMotionThreshold

///滚动模拟的公差
///指定距离、持续时间和速度差应视为平等的差异的结构。
Tolerance get tolerance

SingleChildScrollView

类似Android中的ScrollView,处理简单可滑动的页面布局视图,当内容足够多时,一屏显示不下时,就需要滑动处理。

1
2
3
4
5
6
7
8
9
10
const SingleChildScrollView({
Key key,
this.scrollDirection = Axis.vertical,//设置视图的滚动方向(默认垂直方向), 需要对应的设置其子 Widget 是 Column 或者 Row, 否则会报 Overflow 错误。
this.reverse = false,//是否按照阅读方向相反的方向滑动。若 reverse: false,则滚动内容头部和左侧对其, 那么滑动方向就是从左向右
this.padding,
bool primary,//是否使用默认的 controller
this.physics,//此属性接受一个 ScrollPhysics 对象,它决定可滚动 Widget 如何响应用户操作,比如用户滑动完抬起手指后,继续执行动画;或者滑动到边界时,如何显示。ClampingScrollPhysics:安卓下微光效果。ClampingScrollPhysics:安卓下微光效果。
this.controller,
this.child,
})

CustomScrollView:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const CustomScrollView({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
Key center,
double anchor = 0.0,
double cacheExtent,//
this.slivers = const <Widget>[], //Sliver家族列表
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
})

NestedScrollView:滑动可折叠部分内容,配合Sliver相关组件使用。帮我们解决滑动冲突的控件。通过为外部 ScrollView 和内部 ScrollView 提供自定义 ScrollController 来解决此问题,将它们链接在一起,以便它们作为一个连贯的滚动视图显示给用户。

1
2
3
4
5
6
7
8
9
10
11
const NestedScrollView({
Key key,
this.controller,
this.scrollDirection = Axis.vertical,
this.reverse = false,
this.physics,
@required this.headerSliverBuilder,
@required this.body,
this.dragStartBehavior = DragStartBehavior.start,
})

什么是Sliver?

通常指具有特定滚动效果的可滚动块,可滚动 widget,如 ListView、GridView 等都有对应的 Sliver 实现如 SliverList、SliverGrid 等。

对于大多数 Sliver 来说,它们和可滚动 Widget 最主要的区别是 Sliver 不会包含 Scrollable,也就是说 Sliver 本身不包含滚动交互模型。

正因如此,CustomScrollView 才可以将多个 Sliver “粘”在一起,这些 Sliver 共用 CustomScrollView 的 Scrollable,最终实现统一的滑动效果。

  • CustomScrollView:所有Sliver的根源
  • SliverList:展示线性列表
  • SliverFixedExtentList:固定高度的线性列表
  • SliverPrototypeExtentlist:
  • SliverGrid
  • SliverPadding
  • SliverPersistentHeader:吸顶效果控件
  • SliverAppBar:
  • SliverToBoxAdapter:
  • SliverSafeArea:
  • SliverFillRemaining:
  • SliverOverlapAbsorber: 配合SliverOverlapInjector使用,解决滑动越过pinned的sliver
  • SliverOverlapAbsorberHandle:

SliverAppBar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const SliverAppBar({
Key key,
this.leading,//导航栏左侧 widget
this.automaticallyImplyLeading = true,//如果 leading 为 null,是否自动实现默认的 leading 按钮
this.title,//导航栏标题
this.actions,//如果 leading 为 null,是否自动实现默认的 leading 按钮
this.flexibleSpace,//可缩放区域
this.bottom,//控件底部固定区域,可以放tabbar等
this.elevation,//控件的 z 坐标顺序?
this.forceElevated = false,
this.backgroundColor,
this.brightness,//状态栏的颜色, 黑白两种, 取值: Brightness.dark
this.iconTheme,//设置导航栏上图标的颜色、透明度、和尺寸信息
this.actionsIconTheme,//action 按钮图标的颜色、透明度、和尺寸信息
this.textTheme,
this.primary = true,//导航栏的内容是否显示在顶部, 状态栏的下面
this.centerTitle,//标题是否居中显示,默认值根据不同的操作系统,显示方式不一样
this.titleSpacing = NavigationToolbar.kMiddleSpacing,
this.expandedHeight,//展开最大高度
this.floating = false,
this.pinned = false,//是否固定在顶部
this.snap = false,
this.shape,//阴影设置
})

SliverPersistentHeader

滑动到顶部可以固定,可以根据滚动而变大变小的组件,SliverAppBar就是基于这个实现的。

  • minHeight:固定高度
  • maxHeight:最大显示高度
  • delegate:SliverPersistentHeaderDelegate
  • pinned:收缩到最小高度的时候SliverPersistentHeader是否可见,
  • true:会以折叠高度固定显示在头部,
  • false:缩小到折叠高度后滑出页面
  • floating:true 的时候下滑先展示SliverPersistentHeader介绍,展示完成后才展示其他滑动组件内容

SliverPersistentHeaderDelegate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
abstract class SliverPersistentHeaderDelegate {

// header 最大高度;pined为 true 时,当 header 刚刚固定到顶部时高度为最大高度。
double get maxExtent;

// header 的最小高度;pined为true时,当header固定到顶部,用户继续往上滑动时,header
// 的高度会随着用户继续上滑从 maxExtent 逐渐减小到 minExtent
double get minExtent;

// 构建 header。
// shrinkOffset取值范围[0,maxExtent],当header刚刚到达顶部时,shrinkOffset 值为0,
// 如果用户继续向上滑动列表,shrinkOffset的值会随着用户滑动的偏移减小,直到减到0时。
//
// overlapsContent:一般不建议使用,在使用时一定要小心,后面会解释。
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent);

// header 是否需要重新构建;通常当父级的 StatefulWidget 更新状态时会触发。
// 一般来说只有当 Delegate 的配置发生变化时,应该返回false,比如新旧的 minExtent、maxExtent
// 等其他配置不同时需要返回 true,其余情况返回 false 即可。
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate);

}

SliverList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const SliverList({
Key key,
@required SliverChildDelegate delegate,
})


SliverChildListDelegate(
this.children, {
this.addAutomaticKeepAlives = true,
this.addRepaintBoundaries = true,
this.addSemanticIndexes = true,
this.semanticIndexCallback = _kDefaultSemanticIndexCallback,
this.semanticIndexOffset = 0,
})


SliverToBoxAdapter

用于包装非Sliver类型的Widget

SliverFillRemaining

填充剩余部分

  • hasScrollBody表示内容是否可以滚动,如果是为false,则只是填补空白。

  • fillOverscroll表示子控件是否应该应该伸展以填充超出区域(比如iOS的ListView默认可伸展出一部分区域),当hasScrollBody为false时才起作用。

参考