「Flutter实战」自定义滚动条



历史上的今天

魏文帝曹丕(187年-226年6月29日),字子桓,沛国谯县(今属安徽亳州)人。三国时期曹魏开国皇帝,曹操和卞夫人的嫡长子,之后继承父亲的魏王封号与丞相的大权,最终东汉皇帝汉献帝禅让于其,曹丕登基后改国号为魏,史称曹魏,226年驾崩,谥文皇帝。

正文

默认情况下,Flutter 的滚动组件(比如 ListView)没有显示滚动条,使用 Scrollbar 显示滚动条:

<code>Scrollbar( child: ListView.builder( reverse: false, itemBuilder: (BuildContext context, int index) { return Card( child: Container( height: 45, alignment: Alignment.center, child: Text('$index'), ), ); }, itemCount: 30, itemExtent: 50, ), ) /<code>

在滑动的过程中,右侧显示滚动条,然而 Scrollbar 无法实现自定义滚动条的样式,比如实现如下滚动条样式,

这时需要自定义一个滚动条组件。

实现自定义滚动条组件首先需要监听滚动组件 滚动的位置

,使用 NotificationListener 监听滚动的位置:

<code>bool _handleScrollNotification(ScrollNotification notification) { final ScrollMetrics metrics = notification.metrics; print('滚动组件最大滚动距离:${metrics.maxScrollExtent}'); print('当前滚动位置:${metrics.pixels}'); return true; } @override Widget build(BuildContext context) { return NotificationListener( onNotification: _handleScrollNotification, child: ListView.builder( reverse: false, itemBuilder: (BuildContext context, int index) { return Card( child: Container( height: 45, alignment: Alignment.center, child: Text('$index'), ), ); }, itemCount: 30, itemExtent: 50, ), ); } /<code>

通过 ScrollNotification 获取当前滚动组件最大滚动距离和当前滚动位置,其中 metrics.maxScrollExtent 表示当前滚动组件最大滚动距离,metrics.pixels 表示当前滚动位置。

通过这两个值计算滚动条在当前屏幕的位置,通过 Stack 组件 将 ListView 和 自定义的滚动条进行叠加显示:

<code>NotificationListener( onNotification: _handleScrollNotification, child: Stack( alignment: Alignment.topRight, children: [ ListView.builder( reverse: false, itemBuilder: (BuildContext context, int index) { return Card( child: Container( height: 45, alignment: Alignment.center, child: Text('$index'), ), ); }, itemCount: 30, itemExtent: 50, ), //滚动条 Container( height: 100, width: 20, color: Colors.red, ) ], ), ) /<code>

将此滚动条和 NotificationListener 监听到的滚动事件联动,通过 Container 的 alignment 属性控制滚动条的位置:

<code>Container( alignment: Alignment(1, _alignmentY), padding: EdgeInsets.only(right: 5), child: Container( height: 100, width: 20, color: Colors.red, ), ) /<code>

_alignmentY 就是计算出的偏移位置,计算方法如下:

<code>_alignmentY = -1 + (metrics.pixels / metrics.maxScrollExtent) * 2; /<code>

这里要注意 alignment 的坐标系:

最终效果:

然后只需修改滚动条的样式即可:

<code>class _ScrollBar extends StatelessWidget { @override Widget build(BuildContext context) { return Container( width: 18, height: 60, decoration: BoxDecoration( shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(20)), color: Colors.blue), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.arrow_drop_up, size: 18, ), Icon( Icons.arrow_drop_down, size: 18, ), ], ), ); } } /<code>

最后将代码封装,就可以给所有的滚动组件添加自定义的滚动条,而不仅仅是 ListView。

本文使用 mdnice 排版