側欄 Drawer


Scaffold 可以設定 drawerendDrawer 特性,一般是用來做左、右側欄時使用,通常會搭配 Drawer 使用。例如:

import 'package:flutter/material.dart';

void main() => runApp(
  MaterialApp(
    home: Scaffold(
      appBar: AppBar(
        title: Text('Openhome.cc'),
      ),
      drawer: Drawer(),
    )
  )
);

就這麼簡單,要先認識的並不是 Drawer,而是 Scaffold 本身提供的側欄行為,以設定了 drawer 來說,預設情況下,你可以從螢幕左側往中拉,如果 Scaffold 有設定 AppBar,也可以按下 leading 處的按鈕,兩個方式都會顯示側欄,按側欄範圍外的任意位置,就可以收起側欄。

側欄 Drawer

drawerendDrawer 接受的都是 Widget 型態,也就是說,就算不使用 Drawer,任何 Widget 基本上都可以有這個行為,這表示你的側欄有很高的自訂性;當然,Drawer 有一些基本設定是蠻方便的,例如可自行判斷螢幕空間,決定自身大小之類的,沒特別的需求下,使用它還是蠻方便的。

Scaffold 有幾個與側欄相關的特性,方才談到可以用拖曳來打開側欄,ScaffolddrawerDragStartBehavior 可以決定手勢操作何時被視為拖曳行為,預設值是 DragStartBehavior.start,也就是螢幕邊偵測到手勢操作就視為拖曳開始,設為 DragStartBehavior.down 的話,就是在 down 事件時才視為拖曳開始,這影響的主要應該是動畫的時機之類的吧!

drawerEdgeDragWidth 預設是 20 像素,用來設定螢幕邊手勢操作引發側欄的範圍;drawerScrimColor 可以設定側欄未涵蓋區域的顏色,預設是 Colors.black54

(在撰寫這段文字的同時,API 文件上有個 drawerEnableOpenDragGesture,可用來停用拖曳操作側欄,不過我寫文件時,是固定在 flutter_windows_v1.12.13+hotfix.9-stable.zip 這個版本,drawerEnableOpenDragGesture 應該是後續版本中提供的,從 PR 來看,時間點是 2020 年 2 月 20 日後加入的。)

知道 Scaffold 的側欄基本行為後,接著就看你怎麼設計側欄了,常見的是使用 Drawer 搭配 ListViewDrawerHeader 等來設計。例如:

import 'package:flutter/material.dart';

void main() => runApp(
  MaterialApp(
    home: Home()
  )
);

class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _Home();
}

class _Home extends State<Home> {
  final books = [
    Book(
      imgSrc: 'https://openhome.cc/Gossip/images/ACL059300.jpg',
      name: 'Java SE 14 技術手冊',
    ),
    Book(
      imgSrc: 'https://openhome.cc/Gossip/images/ACL054400.jpg',
      name: 'Python 3.7 技術手冊',
    ),
    Book(
      imgSrc: 'https://openhome.cc/Gossip/images/AEL022800.jpg',
      name: 'JavaScript 技術手冊',
    )
  ];

  final titles = ['Java', 'Python', 'JavaScript'];

  var bookIdx;

  @override
  void initState() {
    bookIdx = 0;
    super.initState();
  }

  void page(idx) {
    setState(() {
      bookIdx = idx;
    });
  }

  @override
  Widget build(BuildContext context) {
    final children = List<Widget>();

    children.add(DrawerHeader(
      child: Text(
        '著作',
        style: TextStyle(
            color: Colors.white
        ),
      ),
      decoration: BoxDecoration(
        color: Colors.blue,
      ),
    ));

    for(var i = 0; i < titles.length; i++) {
      children.add(ListTile(
          title: Text(titles[i]),
          onTap: () {
            page(i);
            // 彈出路由
            Navigator.pop(context);
          }
      ));
    }

    return Scaffold(
      appBar: AppBar(
        title: Text('Openhome.cc'),
      ),
      body: books[bookIdx],
      drawer: Drawer(
        child: ListView(
          children: children,
        )
      ),
    );
  }
}

class Book extends StatelessWidget {
  final String imgSrc;
  final String name;

  Book({this.imgSrc, this.name});

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Center(
          child: Image.network(imgSrc),
        ),
        Text(name)
      ],
    );
  }
}

程式碼看來很長,其實只是在組合 UI 罷了,最重要的地方在於,Scaffold 使用路由堆疊來管理側欄,因此在點選側欄中的項目時,透過 Navigator.pop(context) 之類的操作彈出路由,這時側欄就會退出,來看一下操作效果:

側欄 Drawer