霸圖鎮(zhèn)樓
仿閑魚(yú)底部導(dǎo)航欄帶有中間凸起圖標(biāo)
需求情景: 需要實(shí)現(xiàn)一個(gè)類似閑魚(yú)APP的底部導(dǎo)航欄的實(shí)現(xiàn)
Demo地址 : https://github.com/hanlin19900610/flutter_bottom_navigation_bar
要實(shí)現(xiàn)的效果如圖
好的,下面開(kāi)始上代碼了:
一. 在main.dart文件中,定義APP的入口信息
import 'package:flutter/material.dart';import 'pages/MainPage.dart';void main() => runApp(LightLanguageClient());class LightLanguageClient extends StatelessWidget{ @override
Widget build(BuildContext context) { return MaterialApp(
title: 'Demo',
theme: ThemeData(
primarySwatch: Colors.blue
),
home: MainPage(),
);
}
}
二. 我們需要定義三個(gè)頁(yè)面,功能類似Android的Fragment,分別為HomePage.dart, AssistantPage.dart,MinePage.dart, 這三個(gè)頁(yè)面的代碼很簡(jiǎn)單:
import 'package:flutter/material.dart';class HomePage extends StatefulWidget{ @override
State<StatefulWidget> createState() { return _HomePageState();
}
}class _HomePageState extends State<HomePage>{ @override
Widget build(BuildContext context) { return MaterialApp(
home: Scaffold(
body: Center(
child: Text('這是首頁(yè)'),
),
),
);
}
}
這個(gè)三個(gè)頁(yè)面的代碼都一樣就沒(méi)有都貼出來(lái)
三.現(xiàn)在我們就需要去創(chuàng)建我們的主頁(yè)了,”MainPage.dart”文件
第一步,我們先去實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的底部導(dǎo)航欄
import 'package:flutter/material.dart';import 'HomePage.dart';import 'AssistantPage.dart';import 'MinePage.dart';class MainPage extends StatefulWidget { @override
State<StatefulWidget> createState() { return _MainPage();
}
}class _MainPage extends State<MainPage> with SingleTickerProviderStateMixin {
PageController pageController; int page = 0; @override
Widget build(BuildContext context) { return MaterialApp(
home: Scaffold(
body: new PageView(
children: <Widget>[HomePage(), AssistantPage(), MinePage()],
controller: pageController,
onPageChanged: onPageChanged,
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('首頁(yè)')),
BottomNavigationBarItem(icon: Icon(Icons.assessment), title: Text('助手')),
BottomNavigationBarItem(icon: Icon(Icons.person), title: Text('我的')),
],
onTap: onTap,
currentIndex: page,
),
));
} @override
void initState() { super.initState();
pageController = PageController(initialPage: this.page);
} @override
void dispose() { super.dispose();
pageController.dispose();
} void onTap(int index) {
pageController.animateToPage(index,
duration: const Duration(milliseconds: 300), curve: Curves.ease);
} void onPageChanged(int page) {
setState(() { this.page = page;
});
}
}
在MainPage.dart中我們用到了幾個(gè)控件:
1. PageView : 此控件類似Android的ViewPager,把之前創(chuàng)建的3個(gè)頁(yè)面一次添加進(jìn)去,之后需要給PageView設(shè)置一個(gè)控制器-PageController,給PageView設(shè)置一個(gè)onPageChanged頁(yè)面切換監(jiān)聽(tīng)方法,此方法的功能類似與Android中ViewPager中的OnPageChangeListener里的監(jiān)聽(tīng)方法
2. BottomNavigationBar :此控件主要用于配置底部導(dǎo)航欄,詳細(xì)用法請(qǐng)參見(jiàn)官方文檔,在此控件的使用中,我們需要設(shè)置三個(gè)屬性,
items: 添加底部導(dǎo)航欄的每個(gè)Item
onTap: 為底部導(dǎo)航欄設(shè)置點(diǎn)擊事件
currentIndex: 為底部導(dǎo)航設(shè)置當(dāng)前選中項(xiàng)
四.我們要實(shí)現(xiàn)仿閑魚(yú)的底部導(dǎo)航欄,需要重構(gòu)一下底部導(dǎo)航欄,
重構(gòu)方案:
1.把中間的文字去掉
2.在BottomNavigationBar控件的中上的位置放入一個(gè)圖片
3.重構(gòu)底部導(dǎo)航的事件方法
4.禁止PageView的滑動(dòng)事件
現(xiàn)在開(kāi)始重構(gòu):
1.要在BottomNavigationBar上面覆蓋一個(gè)圖片,我們需要用到一個(gè)布局Widget—-Stack,類似于Framelayout
class _MainPage extends State<MainPage> with SingleTickerProviderStateMixin {
PageController pageController; int page = 0; //添加圖片地址,需要?jiǎng)討B(tài)更換圖片
String bigImg = 'images/home_green.png'; @override
Widget build(BuildContext context) { return MaterialApp(
home: Scaffold(
body: new PageView(
children: <Widget>[HomePage(), AssistantPage(), MinePage()],
controller: pageController,
onPageChanged: onPageChanged,
), //重構(gòu)bottomNavigationBar
bottomNavigationBar: Stack(
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('首頁(yè)')),
BottomNavigationBarItem(icon: Icon(Icons.assessment), title: Text('')),
BottomNavigationBarItem(icon: Icon(Icons.person), title: Text('我的')),
],
onTap: onTap,
currentIndex: page,
),
),
Align(
alignment: Alignment.bottomCenter,
child: InkWell(
child: new Image.asset(bigImg,width: 80.0,height: 80.0,),
onTap:onBigImgTap,
),
)
],
)
));
} @override
void initState() { super.initState();
pageController = PageController(initialPage: this.page);
} @override
void dispose() { super.dispose();
pageController.dispose();
} //修改bottomNavigationBar的點(diǎn)擊事件
void onTap(int index) { if (index != 1) {
setState(() { this.bigImg = 'images/home_green.png';
});
}
pageController.animateToPage(index,
duration: const Duration(milliseconds: 300), curve: Curves.ease);
} //添加圖片的點(diǎn)擊事件
void onBigImgTap() {
setState(() { this.page = 1; this.bigImg = 'images/icon_home.png';
onTap(1);
});
} void onPageChanged(int page) {
setState(() { this.page = page;
});
}
}
重構(gòu)完成之后,效果圖如下,我們發(fā)現(xiàn)這并不是我們想要的,底部導(dǎo)航欄我們是實(shí)現(xiàn)了,但是PageView被遮蓋了
PageView被遮蓋的解決辦法,我們給Stack添加一個(gè)可以指定高度的父級(jí)—Container,修改的代碼如下:
bottomNavigationBar: Container(
height: 100.0,
child: Stack(
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home), title: Text('首頁(yè)')),
BottomNavigationBarItem(
icon: Icon(Icons.accessibility_new), title: Text('')),
BottomNavigationBarItem(
icon: Icon(Icons.person), title: Text('我的')),
],
onTap: onTap,
currentIndex: page,
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: InkWell(
child: new Image.asset(
bigImg,
width: 80.0,
height: 80.0,
),
onTap: onBigImgTap,
),
)),
],
),
)
然后我們需要禁止PageView的滑動(dòng),我們只需要在給PageView設(shè)置一個(gè)屬性就好了
physics: NeverScrollableScrollPhysics(),
在運(yùn)行Flutter項(xiàng)目的時(shí)候出現(xiàn)了一個(gè)問(wèn)題,運(yùn)行時(shí)會(huì)出現(xiàn)一段時(shí)間的白屏,解決辦法:
解決方案很簡(jiǎn)單,Android原生的白屏問(wèn)題可以通過(guò)為 Launcher Activity 設(shè)置 windowBackground 解決,而 Flutter 也是基于此辦法,同時(shí)優(yōu)化了 Flutter 初始化階段的白屏問(wèn)題(覆蓋一個(gè)launchView),只用兩步設(shè)置便能解決 Flutter 中白屏問(wèn)題。
在項(xiàng)目的 android/app/src/main/res/mipmap-xhdpi/ 目錄下添加閃屏圖片;
打開(kāi) android/app/src/main/res/drawable/launch_background.xml 文件,這個(gè)文件就是閃屏的背景文件,具體如何設(shè)置可以查閱 Android Drawable,我在 demo 中的設(shè)置如下:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/background_dark" />
<!-- You can insert your own image assets here -->
<item
android:bottom="84dp">
<bitmap
android:src="@mipmap/launch_image" />
</item>
</layer-list>
如此一來(lái),我們就完成了,文章開(kāi)始提出的需求了.
Demo地址 : https://github.com/hanlin19900610/flutter_bottom_navigation_bar
剛開(kāi)始接觸Flutter,代碼寫(xiě)的有些混亂,可能有些問(wèn)題考慮不是很深入,有不足之處,還請(qǐng)各位大佬指出