「Flutter實戰」自定義滾動條


「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>
「Flutter實戰」自定義滾動條

在滑動的過程中,右側顯示滾動條,然而 Scrollbar 無法實現自定義滾動條的樣式,比如實現如下滾動條樣式,

「Flutter實戰」自定義滾動條

這時需要自定義一個滾動條組件。

實現自定義滾動條組件首先需要監聽滾動組件 滾動的位置,使用 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>
「Flutter實戰」自定義滾動條

將此滾動條和 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 的座標系:

「Flutter實戰」自定義滾動條

最終效果:

「Flutter實戰」自定義滾動條

然後只需修改滾動條的樣式即可:

<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>
「Flutter實戰」自定義滾動條

最後將代碼封裝,就可以給所有的滾動組件添加自定義的滾動條,而不僅僅是 ListView。

本文使用 mdnice 排版


分享到:


相關文章: