Flutter創(chuàng)建底部導(dǎo)航的方式:推薦第三種
一、BottomNavigationBar + BottomNavigationBarItem
- 優(yōu)缺點(diǎn):
- 實(shí)現(xiàn)簡(jiǎn)單,代碼量很少基本就能完成
- 不能調(diào)整
item的文字和圖片間距 - 每次切換頁(yè)面會(huì)重繪,不會(huì)保存之前的頁(yè)面狀態(tài)
具體實(shí)現(xiàn):
///根頁(yè)面
class MainPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return MainPageState();
}
}
class MainPageState extends State <MainPage> {
int currentIndex = 0;
final items = [
BottomNavigationBarItem(icon: Image(image: AssetImage('images/Home.png')), title: Text
('首頁(yè)'),activeIcon: Image(image:
AssetImage
('images/HomeSelect'
'.png'))),
BottomNavigationBarItem(icon: Image(image: AssetImage('images/market_normal.png')),
title:
Text("行情"),activeIcon: Image(image: AssetImage('images/market_selected.png'))),
BottomNavigationBarItem(icon: Image(image: AssetImage('images/transcation_normal.png')),
title: Text
("交易"),activeIcon: Image
(image:
AssetImage('images/transcation_selected.png'),)),
BottomNavigationBarItem(icon: Image(image: AssetImage('images/assets_normal.png')),
title: Text
("資產(chǎn)"),activeIcon: Image(image:
AssetImage('images/assets_selected.png'),)),
];
///4個(gè)tabbar頁(yè)面
final bodyLists = [
HomepageWidget(),
MarketpageWidget(),
TranscationpageWidget(),
AssetspageWidget()
];
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold (
appBar: AppBar(title: Text("切換"),),
bottomNavigationBar: BottomNavigationBar(
items: items,
currentIndex: currentIndex,
onTap: onTap,
unselectedItemColor: prefix1.Color(0xFF989D9D),
selectedItemColor: prefix1.Color(0xFF333333),
type: BottomNavigationBarType.fixed,
),
body: bodyLists[currentIndex],
);
}
void onTap(int index) {
setState(() {
currentIndex = index;
});
}
}
每個(gè)子頁(yè)面代碼都是一樣的,主要是為了測(cè)試每個(gè)頁(yè)面的狀態(tài)
import 'package:flutter/material.dart';
class HomepageWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return HomepageWidgetState();
}
}
class HomepageWidgetState extends State <HomepageWidget> {
int count = 0;
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Center(
child: Text("首頁(yè) $count",style: TextStyle(color: Colors.blue),),
),
floatingActionButton: FloatingActionButton(
onPressed: addButtonClick,
child: Icon(Icons.add),
),
);
}
void addButtonClick() {
setState(() {
count ++;
});
}
}

Oct-23-2019 13-59-00.gif
如上圖,我們可以看到當(dāng)切換界面的時(shí)候,之前的狀態(tài)是被重置了的。那么下面介紹該如何保持狀態(tài)。
二、BottomNavigationBar +Stack + OffStage
- 優(yōu)缺點(diǎn)
- 能夠保存頁(yè)面的狀態(tài)
- 程序初始化的時(shí)候所有的
child都會(huì)執(zhí)行initState(有時(shí)候我們想要的效果是當(dāng)點(diǎn)擊tabbar的時(shí)候界面再去加載) - 使用
BottomNavigationBarItem無(wú)法調(diào)整文字圖片間距
body: Stack(
children: [
_stackOffstage(0),
_stackOffstage(1),
_stackOffstage(2),
_stackOffstage(3)
],
),
//根據(jù)點(diǎn)擊的索引返回widget
Widget _stackOffstage(int index) {
return Offstage(
//If false, the child is included in the tree as normal.
offstage: currentIndex != index,
child: TickerMode(
child: bodyLists[index],
//If true, then tickers in this subtree will tick.
enabled: currentIndex == index,
),
);
}

Oct-23-2019 15-37-09.gif
三、使用PageView + AutomaticKeepAliveClientMixin
- 優(yōu)點(diǎn)
- 可以保存頁(yè)面狀態(tài)
- 每次點(diǎn)擊才會(huì)初始化,而且只會(huì)初始化一次
-
push pop回來(lái)widgetState也不會(huì)重新initState
var pageController = PageController();
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold (
appBar: AppBar(title: Text("切換"),),
bottomNavigationBar: BottomNavigationBar(
items: items,
currentIndex: currentIndex,
onTap: onTap,
unselectedItemColor: prefix1.Color(0xFF989D9D),
selectedItemColor: prefix1.Color(0xFF333333),
type: BottomNavigationBarType.fixed,
),
body: PageView(
controller: pageController,
children: bodyLists,
onPageChanged:pageControllerTap ,
physics: NeverScrollableScrollPhysics(),
),
);
}
void pageControllerTap(int index) {
setState(() {
currentIndex = index;
});
}
void onTap(int index) {
pageController.jumpToPage(index);
}
其中子頁(yè)面的代碼,需要注意 AutomaticKeepAliveClientMixin, 重寫方法
wantKeepAlive ,這樣是讓頁(yè)面一直保存在內(nèi)存中。還有一個(gè)要注意的點(diǎn):super.build(context);,如果不寫,當(dāng)我們使用Navigator.push切換界面的時(shí)候,再pop回來(lái),HomepageWidgetState還是會(huì)執(zhí)行initState,加上super后就不會(huì)再有這個(gè)問題。
class HomepageWidgetState extends State <HomepageWidget> with AutomaticKeepAliveClientMixin {
@override
void initState() {
// TODO: implement initState
super.initState();
print('HomepageWidgetState initState');
}
int count = 0;
@override
Widget build(BuildContext context) {
// TODO: implement build
super.build(context); //注意:每個(gè)子頁(yè)面要寫`super`
return Scaffold(
body: Center(
child: Text("首頁(yè) $count",style: TextStyle(color: Colors.blue),),
),
floatingActionButton: FloatingActionButton(
onPressed: addButtonClick,
child: Icon(Icons.add),
),
);
}
void addButtonClick() {
setState(() {
count ++;
});
}
//一直保存在內(nèi)存中
@override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}
其中PageController是可以控制pageView中要顯示的界面
physics //How the page view should respond to user input. 用戶如何響應(yīng),具體行為可以查看源碼,我們這里可以禁止滑動(dòng)NeverScrollableScrollPhysics
[ScrollPhysics], which can be used instead of this class when the default
/// behavior is desired instead.
/// * [BouncingScrollPhysics], which provides the bouncing overscroll behavior
/// found on iOS.
/// * [ClampingScrollPhysics], which provides the clamping overscroll behavior
/// found on Android.

pageView .gif