【Flutter 组件集录】Scrollbar| 8月更文挑战

2022-03-18 15:25:35 浏览数 (1)

一、Scrollbar 的使用
1. Scrollbar 的效果

ListView 这种可滑动的组件中,默认情况没有右侧的指示器 ,这样用户在滑动中不太容易知道滑动进度。使用 Scrollbar 就可以在 右侧 出现滑动条。如下分别是在 AndroidiOS 的效果,可以看出在不同平台上,Scrollbar 的展示是有所差异的,比如圆角、高度、宽度等。这些我们都能从源码中找到根源。

从使用的角度来看,Scrollbar 非常简单,只是在 ListView 外层嵌套一下就行了。然后滑动时就会发现有滚动指示器,这看起来非常神奇。神奇的点在于: ListView 的滑动没有和 Scrollbar 有任何的直接联系, Scrollbar 竟然可以跟随 ListView 的进行滑动。

代码语言:javascript复制
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 个 参数,这里先看一下 isAlwaysShownthicknessradius 三个决定 Scrollbar 显示的属性。

代码语言:javascript复制
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:falseinteractive:true 的效果。它的作用很明显:如果为 true 时,小滑块可以接受拖动事件,来控制列表的滑动。在移动端默认为 false

5.回调通知:notificationPredicate

notificationPredicate 是一个回调函数,会将 ScrollNotification 对象回调给使用者,并且返回 bool 值决定是否显示 Scrollbar

代码语言:javascript复制
@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 则会报错。你必须保证两者有同一个滑动控制器。通过 滑动控制器 我们可以监听列表的滑动,以及控制滑动。

除此之外,showTrackOnHoverhoverThickness 两个属性顾名思义是悬浮时的效果,这一般只在 非移动端 设备上有效果,另外,目前 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 组件的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~

0 人点赞