0 Scrollbar简介

Scrollbar
是常用的一种控件,几乎所有的UI平台都有这个控件,它可以指示当前在可滚动页面的位置、也可以通过它快速跳转到相应的位置;- 在移动平台,Flutter默认不显示Scrollbar,在Desktop平台,Flutter会为可滚动的控件自动添加scrollbar;
- 将ScrollView包裹在Scrollbar内部,就可以为其添加显示Scrollbar;
Flutter官方关于Scrollbar控件的介绍、油管视频。
dart
Scrollbar(
child: ListView.builder(
primary: true,
itemCount: 20,
itemBuilder: (BuildContext context, int index) {
return Column(
children: [
SizedBox(
width: MediaQuery.of(context).size.width,
child: Center(
child: Text(
'Item $index',
style: const TextStyle(fontSize: 20),
),
),
),
const Divider(
color: Colors.black,
endIndent: 20,
)
],
);
},
),
)

1 Scrollbar建模与算法

建模:
- 视窗区域的高度:
viewportDimension
- 可滚动控件内容的长度:
totalContentExtent
- 当前可滚动控件的滚动位移:
offset
算法实现:
- thumb的长度thumbExtent:
(viewportDimension / totalContentExtent) * viewportDimension
- 可滚动的长度scrollableExtent:
totalContentExtent - viewportDimension
- thumb的起始位置偏移:thumbOffset:
(offset / scrollableExtent) * (viewportDimension - thumbExtent)
2 Flutter的实现
Flutter框架中,当ScrollView
发生滚动时候,会向上发送ScrollNotification
通知,通知中有ScrollMetrics
属性,根据这个属性就可以绘制Scrollbar了,是不是很简单呢?
dart
return FixedScrollMetrics(
minScrollExtent: minScrollExtent ?? (hasContentDimensions ? this.minScrollExtent : null),
maxScrollExtent: maxScrollExtent ?? (hasContentDimensions ? this.maxScrollExtent : null),
pixels: pixels ?? (hasPixels ? this.pixels : null),
viewportDimension: viewportDimension ?? (hasViewportDimension ? this.viewportDimension : null),
axisDirection: axisDirection ?? this.axisDirection,
devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
);
代码解析

RawScrollbarState.build(BuildContext context)
- ① 最内层child下面封装的是ScrollView
- ② CustomPait通过canvas绘制Scrollbar
- ③ MouseRegin响应Desktop平台上鼠标事件
- ④ Scrollbar的手势处理,例如拖动、点击等
- ⑤ 对鼠标滚轮、Trackpad等外设的响应
- ⑥ NotificationListener是对可滚动组件的滚动信息的订阅,用以支持CustiomPait的绘制
RawScrollbarState._handleScrollMetricsNotification()
- 收到ScrollView滚动信息后,调用scrollbarPainter.update()更新CustomPaint的painter信息

ScrollbarPainter.update()
- ① scrollbarPainter存储最新的滚动信息
- ② 通知RenderCustomPaint需要重新绘制

ScrollbarPainter.paint()
这里的算法就是我们前面讲到的算法实现:
- ① 计算Scrollbar的thumb的尺寸
- ② 计算thumb的起始位置偏移
- ③ 通过canvas绘制Scrollbar

- ① 先绘制Track区域,支持圆角、非圆角、Border;
- ② 再绘制thumb区域,支持圆角、非圆角、和自定义形状;
3 总结
综上,Flutter的Scrollbar工作和渲染原理如下:

当前Flutter提供的Scrollbar控件还不支持前后的控制Button,如果开发者想使用此功能,还需要通过三方库或者自定义实现。了解了Scrollbar的工作原理,有助于开发者更好的使用Scrollbar控件,解决使用过程中出现的问题,也方便开发者开发自定义的Scrollbar组件。
作者长期活跃在Flutter开源社区,欢迎大家一起参与开源社区的共建,如果您也有意愿参与Flutter社区的贡献,可以与作者联系。-->GITHUB
您也许还对这些Flutter技术分享感兴趣: