Scaffold
有個 bottomNavigationBar
特性,從名稱上來看,似乎是接受 Flutter 的 BottomNavigationBar
,不過其實它接受的是 Widget
。
其實到目前也看過很多這種特性了,在 Flutter 的在慣例上,許多特性的名稱並不是指可以接受哪個型態,而是告訴你,這邊放的 UI 元件,通常會是做什麼用,例如,bottomNavigationBar
特性告訴你的是,這邊通常會做為底部導覽列。
當然,你想放別的東西,也是沒關係的,例如,故意放個 TabBar
,像是把〈分頁工具列 TabBar〉中分頁放到底部:
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: Home()
)
);
class Home extends StatefulWidget {
@override
State<StatefulWidget> createState() => _HomeState();
}
class _HomeState extends State<Home> with SingleTickerProviderStateMixin{
TabController tabController;
@override
void initState() {
tabController = new TabController(length: 3, vsync: this);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: TabBarView(
controller: tabController,
children: [
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 技術手冊',
),
],
),
// TabBar 指定給 bottomNavigationBar
bottomNavigationBar: Container(
color: Colors.blue,
child: TabBar(
tabs: [
Tab(text: 'Java'),
Tab(text: 'Python'),
Tab(text: 'JavaScript'),
],
controller: tabController,
),
),
);
}
}
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 的建立與組合上,Flutter 算是蠻有彈性的,只要你瞭解 Flutter 的架構,基本上想一切從頭建立自己的元件也可以,不見得要用它的標準元件。
當然,現成的標準元件可以馬上拿來用,是蠻方便的,元件的特性名稱雖然並不指它可以接受什麼型態,然而通常暗示著,會有個類似名稱的標準元件可以使用,例如 bottomNavigationBar
就暗示著有個 BottomNavigationBar
可以用。
只不過,每個元件都有各自設計的方式,雖然 BottomNavigationBar
感覺有點像是 TabBar
,不過元件間的組合方式並不一樣,或者從另一個角度來看,選擇哪個要視你的應用程式撰寫需求而定,如果合適的話,要將 BottomNavigationBar
放到 AppBar
中作為子元件之一,然後放到畫面上方去,基本上也是做得到的。
這邊還是將 BottomNavigationBar
指定給 bottomNavigationBar
就可以了,來看個例子:
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: Home()
)
);
class Home extends StatefulWidget {
@override
State<StatefulWidget> createState() => _HomeState();
}
class _HomeState 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 技術手冊',
)
];
var bookIdx;
@override
void initState() {
bookIdx = 0;
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
// 依 bookIdx 決定 body 要顯示的元件
body: books[bookIdx],
// 使用 BottomNavigationBar
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.blue,
// 導覽列項目
items: [
// 每個項目是 BottomNavigationBarItem 實例
// 必須設定 icon
BottomNavigationBarItem(title: Text('Java'), icon: Icon(Icons.local_cafe)),
BottomNavigationBarItem(title: Text('Python'), icon: Icon(Icons.local_grocery_store)),
BottomNavigationBarItem(title: Text('JavaScript'), icon: Icon(Icons.local_pizza)),
],
// 選中的項目
currentIndex: bookIdx,
selectedFontSize: 16,
selectedItemColor: Colors.white,
// 點選項目時改變 bookIdx
onTap: (idx) => setState(() {
bookIdx = idx;
}),
),
);
}
}
class Book extends StatelessWidget {
同前一範例…略
}
基本上需要注意的地方,都在註解中說明了,BottomNavigationBar
需要指定圖示,對使用者比較有友善吧!來看一下執行的結果:
可以看到,BottomNavigationBar
本身沒有動畫的效果,這是跟 TabBar
不一樣的地方,就 Material Design 的設計來看,動畫藉由視覺上的效果,讓使用者知道畫面之間的前後關係,這可能會是你選擇 TabBar
或 BottomNavigationBar
的依據之一,BottomNavigationBar
需要指定圖示也是考量之一。
另一方面,就過去 GUI 設計的經驗來說,通常 Tab 分頁會是同一個功能頁面下各個不同細項設定之用,至於導覽嘛!每個頁面彼此之間通常是獨立的吧!當然了,選擇在個人,主要還是看需求而定。