【Flutter 元件集錄】Scrollbar| 8月更文挑戰

張風捷特烈發表於2021-08-04
前言:

這是我參與8月更文挑戰的第 4 天,活動詳情檢視:8月更文挑戰。為應掘金的八月更文挑戰,我準備在本月挑選 31 個以前沒有介紹過的元件,進行全面分析和屬性介紹。這些文章將來會作為 Flutter 元件集錄 的重要素材。希望可以堅持下去,你的支援將是我最大的動力~


一、Scrollbar 的使用

1. Scrollbar 的效果

ListView 這種可滑動的元件中,預設情況沒有右側的指示器 ,這樣使用者在滑動中不太容易知道滑動進度。使用 Scrollbar 就可以在 右側 出現滑動條。如下分別是在 AndroidiOS 的效果,可以看出在不同平臺上,Scrollbar 的展示是有所差異的,比如圓角、高度、寬度等。這些我們都能從原始碼中找到根源。

Android 平臺iOS 平臺

從使用的角度來看,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 個 引數,這裡先看一下 isAlwaysShownthicknessradius 三個決定 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:falseinteractive:true 的效果。它的作用很明顯:如果為 true 時,小滑塊可以接受拖動事件,來控制列表的滑動。在移動端預設為 false

interactive:falseinteractive:true

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 則會報錯。你必須保證兩者有同一個滑動控制器。通過 滑動控制器 我們可以監聽列表的滑動,以及控制滑動。


除此之外,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 元件的使用方式到這裡就介紹完畢,那本文到這裡就結束了,謝謝觀看,明天見~

相關文章