Tabs 一般用于展現(xiàn)多個頁面的內(nèi)容的容器,例如微信的下面標(biāo)簽欄。
常用的組件有:TabBar、TabBarView、TabController(有狀態(tài))、DefaultTabController(無狀態(tài))、BottomNavigationBar(底端欄)。

在 Tabs 里,使用到了動畫效果,因此需要 with 一個類。
// 之前
class HomePageState extends State<HomePage> {
// ...
}
// 使用 Tabs 之后
class HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
// ...
}
接著,在 HomePageState 里初始化 Tabs Controller,這里重寫了兩個生命周期函數(shù)。
class HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
var tabController; // 先聲明變量
@override
void initState() {
super.initState();
this.tabController = new TabController(
vsync: this, // 動畫效果的異步處理
length: 3 // tab 個數(shù)
);
}
// 當(dāng)整個頁面 dispose 時(shí),記得把控制器也 dispose 掉,釋放內(nèi)存
@override
void dispose() {
this.tabController .dispose();
super.dispose();
}
}
接著,添加 TabBar 和 TabBarView。
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('首頁'),
),
body: new TabBarView(
controller: this.tabController, //配置控制器
children: [ // Tab 內(nèi)容
new Text('aaa'),
new Text('bbb'),
new Text('ccc'),
],
),
// 底端欄是一個 TabBar
bottomNavigationBar: new Material(
color: Colors.blue,
child: new TabBar(
controller: this.tabController,
indicatorColor: Colors.white,
tabs: <Tab>[
new Tab(
text: '主頁',
icon: new Icon(Icons.home),
),
new Tab(
text: '歷史',
icon: new Icon(Icons.history),
),
new Tab(
text: '書籍',
icon: new Icon(Icons.book),
),
],
),
)
);
}
可以看到,在 TabBar 里是設(shè)置控制欄的內(nèi)容,在 TabBarView 里是設(shè)置每個 Tab 的內(nèi)容。而 TabController 是一個用于控制的組件。 TabBar 與 TabBarView 的數(shù)組位置一一對應(yīng)。

TabBar
TabBar是用于顯示 Tab 的菜單信息的。
在 TabBar 里有以下常用屬性:
- controller → TabController - 控制組件。
- tabs → List - tabs 列表。
- indicatorColor → Color - 指示線顏色,就是下面的線條。使用 Colors.transparent 可以讓它消失。
- unselectedLabelColor → Color - 未選定標(biāo)簽標(biāo)簽的顏色。
- unselectedLabelStyle → TextStyle - 未選定標(biāo)簽標(biāo)簽的文字樣式。
- labelStyle → TextStyle - 字體樣式。
- labelColor → Color - 字體顏色,默認(rèn)白色。
- isScrollable → bool - 該標(biāo)簽欄是否可以水平滾動,默認(rèn) false。在多個 tab 時(shí)有用。
- indicatorWeight → double - 指示線的大小,必須大于 0。
- indicatorSize → TabBarIndicatorSize - 所選標(biāo)簽指標(biāo)的大小,當(dāng)為 Tab 時(shí)會盡可能大,當(dāng)為 Label 時(shí)會盡可能小。
- indicatorPadding → EdgeInsetsGeometry - 顯示在選定選項(xiàng)卡下方的線的水平填充。

TabBar 沒有可以用于設(shè)置選中項(xiàng)樣式的屬性。那么想設(shè)置選中的樣式怎么弄?可以利用未選中來排除。
bottomNavigationBar: new Material(
color: Colors.white,
child: new TabBar(
controller: this.tabController,
unselectedLabelColor: Colors.black38, // <-- 一定要在 labelColor 前面。
labelColor: Colors.blue,
indicatorColor: Colors.blue,
tabs: <Tab>[],
),
);
把 TabBar 放在頂端,通過 appBar 的 title 設(shè)置。
appBar: new AppBar(
title: new Material( // <-- 放在這里
color: Colors.blue,
child: new TabBar(
controller: this.tabController,
indicatorColor: Colors.transparent,
unselectedLabelColor: Colors.blue[200],
labelColor: Colors.white,
tabs: <Tab>[
new Tab(text: '主頁'),
new Tab(text: '歷史'),
new Tab(text: '書籍'),
],
),
),
),

還可以放在 bottom 處,形成一種雙欄效果。
appBar: new AppBar(
title: new Icon(Icons.menu),
bottom: new TabBar(
controller: this.tabController,
indicatorColor: Colors.transparent,
unselectedLabelColor: Colors.blue[200],
labelColor: Colors.white,
tabs: <Tab>[
new Tab(text: '主頁'),
new Tab(text: '歷史'),
new Tab(text: '書籍'),
],
)
),

TabBarView
TabBarView就是用來展示每個 Tab 的內(nèi)容的,在 TabBarView 的數(shù)組里與 TabBar 的數(shù)組一一映射。
TabBarView 有以下常用屬性:
- children → List - Tab 內(nèi)容列表。
- controller → TabController - Tab 控制器。
- physics → ScrollPhysics - 頁面視圖如何響應(yīng)用戶輸入。
TabController
TabController是 Tab 頁的控制器,用于定義 Tab 標(biāo)簽和內(nèi)容頁的坐標(biāo),還可配置標(biāo)簽頁的切換動畫效果等。
TabController 一般放入有狀態(tài)控件中使用,以適應(yīng)標(biāo)簽頁數(shù)量和內(nèi)容有動態(tài)變化的場景,如果標(biāo)簽頁在 App 中是靜態(tài)固定的格局,則可以在無狀態(tài)控件中加入簡易版的 DefaultTabController 以提高運(yùn)行效率,畢竟無狀態(tài)控件要比有狀態(tài)控件更省資源,運(yùn)行效率更快。
TabController 的使用方式非常直白,它作為 TabBar 和 TabBarView 的控制器使用存在。
class HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
var tabController; // 先聲明變量
@override
void initState() {
super.initState();
this.tabController = new TabController(
vsync: this, // 動畫效果的異步處理
length: 3 // tab 個數(shù)
initialIndex: 0, // 起始位置
);
}
// 當(dāng)整個頁面 dispose 時(shí),記得把控制器也 dispose 掉,釋放內(nèi)存
@override
void dispose() {
this.tabController.dispose();
super.dispose();
}
}
常用的方法和屬性:
- animateTo(int index) - 滑動到某個 tab 頁。
- offset(double val) - 獲取/設(shè)置當(dāng)前 tab 的偏移量(從第一個開始算) 。
- dispose() - 釋放當(dāng)前 tabController。
- addListener() - 監(jiān)聽器。
- indexIsChanging → bool - 返回 tab index 是否已經(jīng)改變了。
new FlatButton(
child: new Text('點(diǎn)我'),
onPressed: () {
this.tabController.animateTo(0);
print(this.tabController.offset());
},
),
那么一般當(dāng) tab 索引改變時(shí),會做一些處理,比如網(wǎng)絡(luò)請求什么的,在這里怎么判斷?使用 addListener。
@override
void initState() {
super.initState();
this.tabController = new TabController(vsync: this, length: 3);
this.tabController.addListener(() {
if (this.tabController.indexIsChanging) {
print('索引改變');
}
});
}
BottomNavigationBar
也可以單獨(dú)使用 BottomNavigationBar,不過這時(shí)候要自己設(shè)置頁面的渲染內(nèi)容。
bottomNavigationBar: new Builder(
builder: (BuildContext context) {
return new BottomNavigationBar(
items: [
new BottomNavigationBarItem(icon: new Icon(Icons.adb), title: new Text('首頁')),
new BottomNavigationBarItem(icon: new Icon(Icons.adb), title: new Text('關(guān)于')),
],
onTap: (index) {
Scaffold.of(context).showSnackBar(
new SnackBar(
content: new Text('Hello'),
),
);
},
);
},
)
