前面我們已經使用 Scaffold
組件很多次了,但對它並沒有過多著墨,其實 Scaffold
還有很多隱藏的功能可以協助我們建置一個更實用的應用程式。這個段落我們將探討一些常用的功能。
其中一個重要的功能就是 drawer
參數提供的,它可以加入一個側邊欄,可以展開和收合藏起來。你應該在很多應用都看過這個效果,看起來好像很難實作。但在 Flutter 這個功能非常簡單。
首先我們需要定義一個 GlobalKey。讓我們可以直接存取 Scaffold
final _scaffoldKey = GlobalKey<ScaffoldState>();
然後將 Key 傳入 Scaffold
Scaffold(
key: _scaffoldKey,
...
)
下一步,我們需要定義我們的 drawer
drawer: Drawer(
child: Column(
children: [
IconButton(
onPressed: () => _scaffoldKey.currentState!.closeDrawer(),
icon: Icon(Icons.close)
),
TextButton(
child: Text("按鈕"),
onPressed: () {},
)
]
),
)
上面是設定 Scaffold
的 drawer
參數,我們使用 Drawer
組件,但這不是必須的,只是因為這個組件相對容易使用而且已經具備一樣合適的樣式。內部的話我們使用 Column
來組織按鈕,這些組件的用法我們已經了解了。其中比較陌生的應該是
onPressed: () => _scaffoldKey.currentState!.closeDrawer(),
當按鈕點擊的時候,利用 _scaffoldKey
讀取 Scaffold
的狀態,然後呼叫 closeDrawer()
。
不過,我們還需要一種方式來開啟這個側邊欄,我們可以在主頁加入按鈕
ElevatedButton(
onPressed: () {
_scaffoldKey.currentState!.openDrawer();
},
child: const Text("打開"),
),
如你所見,開啟和關閉的操作非常類似,都需要取得 Scaffold
的狀態,然後呼叫對應的方法。若需要進一步在進入畫面立即開啟可以使用 DrawerController
控制初始值或在 initState
生命週期方法中呼叫。
這裡,也呼應表單一節提到的
controller
的方式。我們可以預期許多組件應該存在類似的設計和使用方式。
和 drawer
類似的還有 snackbar
功能。在之前的章節有稍微介紹。這是一種簡易的訊息通知,從螢幕下方向上滑出呈現,用於向使用者提供一些訊息反饋,例如操作的結果。
雖然一樣是滑出組件,但兩者的實作差異非常大如下範例:
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('彈出訊息'),
),
);
},
child: Text('顯示'),
)
和 drawer
不同,我們不用通過 Scaffold
的 Key 存取狀態並呼叫對應方法,而是通過 ScaffoldMessenger.of
找到對應的 InheritedWidget
,它會負責處理建立和顯示 Snackbar。
ScaffoldMessenger.of(context)
除了這種方式之外,在無法取得 BuildContext
的情況下還可以使用 GlobalKey
的方式,步驟如下
建立 GlobalKey<ScaffoldMessengerState>
物件:
final GlobalKey<ScaffoldMessengerState> snackbarKey = GlobalKey<ScaffoldMessengerState>();
將 GlobalKey
和 MaterialApp
或 ScaffoldMessenger
關聯:
MaterialApp(
scaffoldMessengerKey: snackbarKey,
home: Scaffold(
body: HomePage(),
),
);
或者與 ScaffoldMessenger
組件關聯,並包住 Scaffold
ScaffoldMessenger(
key: snackbarKey,
child: Scaffold(
body: HomePage(),
),
);
在需要顯示 SnackBar
的地方,使用 GlobalKey
存取 ScaffoldMessengerState
並呼叫 showSnackBar
方法
void showSnackbar(String message) {
snackbarKey.currentState?.showSnackBar(
SnackBar(
content: Text(message),
),
);
}
現在,我們可以在任何地方呼叫 showSnackbar
來顯示 SnackBar
,而不需要通過 BuildContext
。
補充:在 Flutter 1.22 版本之前並沒有
ScaffoldMessenger
而是使用Scaffold.of(context).showSnackBar()
來顯示SnackBar
,此時的SnackBar
是與當前的Scaffold
關聯的。如果我們在一個頁面上顯示了SnackBar
,然後導向另一個頁面,SnackBar
會自動消失。因為當前頁面的Scaffold
被替換為新頁面的Scaffold
,而原來的SnackBar
是與舊的Scaffold
關聯的,所以它會消失。為了解決這個問題,ScaffoldMessenger
因應而生。
前面章節我們簡單的介紹了 ListView
,知道了這個組件可用來呈現列表和支援捲動的功能。但其實它也有一些互相配合的組件 ListTile
。ListTile
簡單的建立一個結構用來放置顯示於列表中的組件,例如 Icon,文字或圖片。你當然可以使用 Container
等組件模仿出 ListTile
的結構,但結構的抽象和相關佈局的程式簡化可以讓你跟專注在應用程式希望提供的內容。下面為 ListTile
的範例:
ListTile(
title: Text('項目'),
subtitle: Text('簡短說明'),
leading: Icon(Icons.location_on),
onTap: () {},
trailing: IconButton(
icon: Icon(Icons.thumb_up),
onPressed: () {},
),
)
leading
組件左邊,一般用來顯示列表項目的 Icon 或圖片。title
粗體較大的文字顯示項目的主要文字。subtitle
在粗體文字下方較小的文字,顯示一些簡單的補充說明。trailing
跟 leading
相反,出現在右邊區塊,通常用來顯示一些對應操作的按鈕。此外,ListTile
還有其他功能:
leading
和 trailing
。isThreeLine
增加 subtitle
的空間擴展至三行。總體來說 ListTile
非常方便,可以在我們第一次排版的時候節省很多時間。
圖片有很多類型,了解什麼情況該使用什麼格式有時並不是那麼單純容易。這裡無法無限展開討論,但希望提供一個概覽,讓你可以比較明智的決定該使用何種類型圖片。
Flutter 預設就支援很多圖片格式:
這些是常見的圖片格式,在它們之間選擇應該是相對容易,除此之外還有一些值得深入理解的格式。
Scalable Vector Graphics 使用 XML 來定義向量圖片。一個好處是無論把圖片放大多少多不會失真。另外 XML 可以無損壓縮。但 SVG 是一個相對複雜的格式,因為其支援 CSS 功能。
**Flutter 預設不支援 SVG 因此使用 SVG 可能會是挑戰。**雖然預設不支援,但你可以使用套件:
flutter_svg
這是一個簡易使用的套件,適用大多數 SVG,載入 SVG 和其他預設圖片格式很接近,例如 SvgPicture.asset(name)
,但是這個套件並不完全支援所有的 SVG 規範和 CSS 樣式。 SVG 的樣式應使用屬性來設定,而不是 CSS 的 style
屬性。不要使用 style="color: blue;"
,應該使用 fill="blue"
來設定顏色。這樣可以確保 SVG 能夠正確呈現。jovial_svg
強大的 SVG 套件,支援 CSS 因此可以支援大多數 SVG,但使用上比較複雜。值得注意的是兩個套件都支援將 SVG 預先編譯為等價二進制檔案的功能,以便更快速的處理圖片。這表示 SVG 在一些需要速度的應用上不是最佳選擇。預先編譯後的檔案可以像內建格式一樣在程式中使用。
Lottie 是一種動畫解決方案,簡單說它們就像是動畫 GIF 和 SVG 的混合體。網路上可以找到許多免費資源非常方便使用。
Lottie 是由 Airbnb 開發的開源的解決方案。基本上設計師使用 After Effect 製作動畫,然後通過 Bodymovin 匯出 Lottie JSON
檔案,然後開發者可以在應用中使用相關函式庫並載入執行匯出的檔案。目前支援 Android,iOS,React Native,Windows,Web 等平台。而 LottieFiles 則是一個與 Lottie 相關的平台,提供許多免費和付費 Lottie 動畫。
在 Flutter 中使用 Lottie
套件也是非常簡單。只要加入一行程式就可以呈現動畫:
Lottie.asset('assets/LottieLogo.json')
只要把想要的 Lottie 檔案加入 assets
目錄,然後就可以使用了。
slivers
是一種特殊的組件,其中會包含一些底層較為複雜的滾動控制邏輯,主要專門處理滾動內容而設計的。初學 slivers
可能會遭遇挫折,因為和我們之前學習的組件思維有點不同。我們之前在使用 ListView
的時候已經看過 slivers 的實際效果了。
一般來說,但我們使用組件的時候通常大小會是固定的,或者由佈局的父元素限制範圍決定。但是當我們加入滾動效果的時候,情況就不同了,滾動表示內容實際上是超出螢幕或範圍的,也就是我們取消了組件大小的限制。
這時候,一個組件放在可垂直捲動的區域內,那麼它就會變成無限高,Flutter 在處理這種不確定大小的組件時就會遇到問題。
因此如果你要使用 slivers
你需要先認真研究理解,如果你不了解它們的工作原理,可能會反覆遇到錯誤,無法正確解決問題。
為了學習關於 slivers
,我們將從一個範例開始。假設我們希望構建一個列表顯示資訊,然後下方有一個下一頁的按鈕。這裡有兩個需求:
這個情境超級常見,但如果沒有 slivers
會非常難實作。讓我們先從範例開始:
Scaffold(
body: CustomScrollView(
slivers: [
SliverFixedExtentList(
itemExtent: 100.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
color: index.isEven ? Colors.red : Colors.black,
);
},
childCount: 3,
),
),
SliverFillRemaining(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
child: Text("下一頁"),
onPressed: () {}
),
],
),
)
]
),
)
在這個範例,我們建立了 Scaffold
裡面包含 CustomScrollView。這個組件引領我們進入 slivers
的世界,目的是讓其包含的東西可以捲動。
顯然,當內容比較多無法用單一螢幕呈現的時候,可捲動是非常重要的功能。CustomScrollView
的 slivers
參數接受一個陣列,這裡我們使用了兩個 Sliver 相關的組件 SliverFixedExtendList
和 SliverFillRemaining
其中前者類似 ListView
後者則是填充底部剩餘空間。
SliverFillRemaining
通常是最後一個 slivers
組件,它可以填充可捲動區域剩下的空間。
SliverFixedExtendList
的部分稍微比較難理解,但基本上就是通過類似組件 builder
並回傳 SliverChildBuilderDelegate
。這個 delegate
委派通過 childCount
設定子元素的數量。接著我們使用 index
來設定不同顏色的 Container
。
最後,回到 SliverFillRemaining
填充的剩餘空間,我們放入一個 Column
和按鈕。
如此一來,在內容沒那麼多的時候,按鈕如我們的需求會出現在底部。當內容變多超出螢幕範圍的時候,按鈕則被推出螢幕,實際上在可捲動區域的底部。
另一個 slivers
常使用的功能就是 SliverAppBar
。類似 Scaffold
的 AppBar
,但它的高可以根據內容偏移而變化,並且可以根據使用者的操作顯示或隱藏。
SliverAppBar
只是另一個 Sliver 組件,因此也可以放在 CustomScrollView
,例如下面範例:
CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 100,0,
floating: true,
title: Text("頁面"),
flexibleSpace: FlexibleSpaceBar(
title: Text("標題")
),
actions: [
IconButton(
icon: Icon(Icons.delete),
onPressed: () {}
),
],
)
]
)
這個範例,SliverAppBar
的高會展開到 100px,並且和 AppBar
一樣有標題和動作按鈕,還有 flexibleSpace
,這個區域在使用者捲動的時候會縮小。此外,設定了 floating
參數,表示當使用者向下捲動時會隱藏,向上捲動時會在重新出現。
除了上述的用法,還有其他控制 SliverAppBar
在捲動時對應的行為。下面是 3 個主要的參數:
floating
控制向上捲動時,是否立即顯示pinned
控制向下捲動時,保持顯示。伸縮區域仍保持展開或收縮。snap
是否快速彈入的效果光使用文字可能很難具體說明這些效果。可以參考官方說明提供的影片可快速理解組件的效果。