一、Scrollbar 的使用
1. Scrollbar 的效果
在ListView
这种可滑动的组件中,默认情况没有右侧的指示器
,这样用户在滑动中不太容易知道滑动进度。使用 Scrollbar
就可以在 右侧
出现滑动条。如下分别是在 Android
和 iOS
的效果,可以看出在不同平台上,Scrollbar
的展示是有所差异的,比如圆角、高度、宽度等。这些我们都能从源码中找到根源。
从使用的角度来看,Scrollbar
非常简单,只是在 ListView
外层嵌套一下就行了。然后滑动时就会发现有滚动指示器,这看起来非常神奇。神奇的点在于: ListView
的滑动没有和 Scrollbar
有任何的直接联系, Scrollbar
竟然可以跟随 ListView
的进行滑动。
class ScrollbarDemo extends StatelessWidget {
const ScrollbarDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scrollbar( //<--- tag1
child: ListView(
children:
List.generate(
60,
(index) => ItemBox(index: index)).toList()),
);
}
}
这种 可插拔
式的组合,既可以让组件间几乎没有耦合
,又可以让一方随另一方进行改变。Scrollbar
虽然在使用上非常简单,但其背后的这套数据通知
方案是非常值得我们去研究学习的。
2. Scrollbar 的表现属性
从下面 Scrollbar
的构造函数中可以看出,除了 child
是必传的入参,还有 8 个 参数,这里先看一下 isAlwaysShown
、thickness
、radius
三个决定 Scrollbar
显示的属性。
Scrollbar(
isAlwaysShown: true, // 是否一直显示
radius: const Radius.circular(3), // 圆角半径
thickness: 6,// 线宽
child: ...
);
如下左侧是 安卓平台默认显示
效果,可以看出 Scrollbar
只在滑动过程中显示出来,并且显隐时伴随 透明渐变动画
效果。如下右侧上面三个属性设置后的效果,isAlwaysShown
表示 Scrollbar
是否一直显示;radius
表示 圆角半径
;thickness
表示 Scrollbar
滑块的宽度。
3. Scrollbar 的尺寸区域
所有可以显示的组件都会尤其占据的位置区域,大家可以思考一下 Scrollbar
的尺寸是 包括 ListView
的整体,还是只是一个细的长条,或只是一个小滑块。通过 布局查看器
可以看出 Scrollbar
的尺寸是包括 ListView
在内的整个一大片。到这里,我们或多或少可以猜到 Scrollbar
源码在布局上的处理。
4.可交互性:interactive
如下两幅图分别是 interactive:false
和 interactive:true
的效果。它的作用很明显:如果为 true
时,小滑块可以接受拖动事件,来控制列表的滑动。在移动端
默认为 false
。
5.回调通知:notificationPredicate
notificationPredicate
是一个回调函数,会将 ScrollNotification
对象回调给使用者,并且返回 bool
值决定是否显示 Scrollbar
。
@override
Widget build(BuildContext context) {
return Scrollbar(
notificationPredicate: _notificationPredicate,
child: ListView(
children:
List.generate(60, (index) => ItemBox(index: index)).toList()),
);
}
bool _notificationPredicate(ScrollNotification notification) {
print('----$notification---------');
return true;
}
6.滑动控制器:controller
如果你只为 ListView
指定了 controller
属性,那么 Scrollbar
则会报错。你必须保证两者有同一个滑动控制器。通过 滑动控制器
我们可以监听列表的滑动,以及控制滑动。
除此之外,showTrackOnHover
和 hoverThickness
两个属性顾名思义是悬浮时的效果,这一般只在 非移动端
设备上有效果,另外,目前 ListView
在桌面端中默认自带 Scrollbar
。
到这里 Scrollbar
所有的属性用法就已经介绍完毕。下面简单地看一下 Scrollbar
的源码实现,不止于是知道怎么用,还能对它的内部机制有一点了解,源码中的一些逻辑处理,这或许在某些场景中能对你产生帮助,多了解一些总没什么坏处。知其然,知其所以然,你把握的才够通透
。
二、Scrollbar 源码简看
1. Scrollbar 类定义
从下面可以看出 Scrollbar
是一个 StatefulWidget
,通过 _ScrollbarState
状态类构建组件。
下面是 _ScrollbarState
的全部代码,通过如果是 iOS
平台,则构建 CupertinoScrollbar
,否则构建 _MaterialScrollbar
。
这是 _ScrollbarState
的全部源码,不过我从这里看不出 Scrollbar 是 StatefulWidget 的必要性
。不知你有什么见解。
2. 滑动事件的监听和滑块的移动
CupertinoScrollbar
和 _MaterialScrollbar
都是继承自 RawScrollbar
,也就是说它们的底层逻辑是一样的,只不过根据平台进行一定的适配。
在 RawScrollbarState
构建组件代码中可以看到,使用了 NotificationListener
监听 ScrollNotification
通知,执行 _handleScrollNotification
方法。如果不了解 NotificationListener
组件,可以看一下第一篇
在 _handleScrollNotification
中有一些比较核心的逻辑,其中 notificationPredicate
回调会先触发,如果该函数返回 false
,也就意味着_handleScrollNotification
返回 false
,下面的逻辑不会被执行。这也是为什么返回 false
时,滑块不显示的原因。下面会执行透明渐变动画,以及根据 notification
信息更新 scrollbarPainter
画板,这是滑块可以跟随列表滑动最核心的处理。
3.滑块的绘制
在 RawScrollbarState#build
方法的最后,是通过前景画板 foregroundPainter
进行绘制,child
为传入的 ListView
,这也是为什么 Scrollbar
的尺寸区域是整个一片的原因。
绘制的画板是 ScrollbarPainter
,在状态初始化时被创建。
ScrollbarPainter
继承自 ChangeNotifier
,并实现 CustomPainter
,也就是说它既是可监听对象,又是画板,也就说明它自己可以通知进行画板重绘,使用这里 ScrollbarPainter
是以成员变量的方式声明的,在需要更新时,自己执行更新。这也很值得我们学习借鉴,源码是最好的老师。至于具体的绘制逻辑,就不说了,有兴趣的自己看看。
Scrollbar 组件
的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~