最近在项目中,UI设计需要实现一个底部凸起的导航栏,一顿摸索,发现Flutter默认的导航栏就可以实现,好开心,一顿ctrl+c,ctrl+v之后,发现,what Fxxk,为什么底部还留出了一块,后来明白,Flutter原生的会默认在底部流出文字的位置,及时设置了也没有用。so,自己来。。。(新手小白,大佬勿喷)
话不多说,有图有真相
1.首先创建了4个page
List _pageList = [
TaskPage(),
WorkBenchPage(),
MessagePage(),
MePage()
];
int lastTime = 0; // 点击后退按钮两次退出应用
PageController pageController;
int _currentIndex = 0;// 当前界面位置
2.定义tab的点击事件,和page的切换
void onPageChanged(int value) {
setState(() {
this._currentIndex = value;
});
}
void onTap(int value) {
setState(() {
this._currentIndex = value;
});
}
3.通过stack和Align实现底部凸起导航
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: WillPopScope(
child: SafeArea(
top: false,
child: Scaffold(
body: this._pageList[_currentIndex],
bottomNavigationBar: Container(
height: 80,
child: Stack(
children: [
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.white,
height: 60,
child: Padding(
padding: new EdgeInsets.fromLTRB(0, 6, 0, 6),
child: Flex(
direction: Axis.horizontal,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
onTap(0);
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
_currentIndex == 0
? "images/ic_tab_task_1.png"
: "images/ic_tab_task_0.png",
width: _currentIndex == 0 ? 25 : 20,
height: _currentIndex == 0 ? 25 : 20,
),
Text("一",
style: TextStyle(
color: _currentIndex == 0
? Colors.blue
: Colors.grey))
],
)),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
onTap(1);
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
_currentIndex == 1
? "images/ic_tab_workbench_1.png"
: "images/ic_tab_workbench_0.png",
width: _currentIndex == 1 ? 25 : 20,
height: _currentIndex == 1 ? 25 : 20,
),
Text("一",
style: TextStyle(
color: _currentIndex == 1
? Colors.blue
: Colors.grey))
],
)),
),
Expanded(
flex: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [],
),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
onTap(2);
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
_currentIndex == 2
? "images/ic_tab_message_1.png"
: "images/ic_tab_message_0.png",
width: _currentIndex == 2 ? 25 : 20,
height: _currentIndex == 2 ? 25 : 20,
),
Text("一",
style: TextStyle(
color: _currentIndex == 2
? Colors.blue
: Colors.grey))
],
)),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
onTap(3);
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
_currentIndex == 3
? "images/ic_tab_me_1.png"
: "images/ic_tab_me_0.png",
width: _currentIndex == 3 ? 25 : 20,
height: _currentIndex == 3 ? 25 : 20,
),
Text("一",
style: TextStyle(
color: _currentIndex == 3
? Colors.blue
: Colors.grey))
],
)),
)
],
),
),
),
),
// 这个就是悬浮出来的按钮
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: GestureDetector(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
padding: new EdgeInsets.all(3),
child: Image.asset(
"images/ic_tab_add.png",
width: 100.0,
height: 100.0,
),
),
onTap: () {
// 点击凸起按钮后,出现一个带动画的dialog
_animationController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this);
_animationController.forward();
MoreTabDialog.show(context,
callBack: this,
animationController: _animationController);
},
),
)),
],
),
),
),
),
onWillPop: () {
// 点击后退退出应用
int newTime = DateTime.now().millisecondsSinceEpoch;
int result = newTime - lastTime;
lastTime = newTime;
if (result > 2000) {
Fluttertoast.showToast(msg: "再按一次退出");
} else {
SystemNavigator.pop();
}
return null;
}),
);
完整代码
1.首页
class _ManagementMainPageState extends State
with TickerProviderStateMixin
implements MoreTabDialogCallBack {
List _pageList = [
TaskPage(),
WorkBenchPage(),
MessagePage(),
MePage()
];
int lastTime = 0;
PageController pageController;
int _currentIndex = 0;
AnimationController _animationController;
@override
void initState() {
pageController = PageController(initialPage: this._currentIndex);
super.initState();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: WillPopScope(
child: SafeArea(
top: false,
child: Scaffold(
body: this._pageList[_currentIndex],
bottomNavigationBar: Container(
height: 80,
child: Stack(
children: [
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.white,
height: 60,
child: Padding(
padding: new EdgeInsets.fromLTRB(0, 6, 0, 6),
child: Flex(
direction: Axis.horizontal,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
onTap(0);
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
_currentIndex == 0
? "images/ic_tab_task_1.png"
: "images/ic_tab_task_0.png",
width: _currentIndex == 0 ? 25 : 20,
height: _currentIndex == 0 ? 25 : 20,
),
Text("一",
style: TextStyle(
color: _currentIndex == 0
? Colors.blue
: Colors.grey))
],
)),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
onTap(1);
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
_currentIndex == 1
? "images/ic_tab_workbench_1.png"
: "images/ic_tab_workbench_0.png",
width: _currentIndex == 1 ? 25 : 20,
height: _currentIndex == 1 ? 25 : 20,
),
Text("一",
style: TextStyle(
color: _currentIndex == 1
? Colors.blue
: Colors.grey))
],
)),
),
Expanded(
flex: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [],
),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
onTap(2);
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
_currentIndex == 2
? "images/ic_tab_message_1.png"
: "images/ic_tab_message_0.png",
width: _currentIndex == 2 ? 25 : 20,
height: _currentIndex == 2 ? 25 : 20,
),
Text("一",
style: TextStyle(
color: _currentIndex == 2
? Colors.blue
: Colors.grey))
],
)),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
onTap(3);
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
_currentIndex == 3
? "images/ic_tab_me_1.png"
: "images/ic_tab_me_0.png",
width: _currentIndex == 3 ? 25 : 20,
height: _currentIndex == 3 ? 25 : 20,
),
Text("一",
style: TextStyle(
color: _currentIndex == 3
? Colors.blue
: Colors.grey))
],
)),
)
],
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: GestureDetector(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
padding: new EdgeInsets.all(3),
child: Image.asset(
"images/ic_tab_add.png",
width: 100.0,
height: 100.0,
),
),
onTap: () {
_animationController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this);
_animationController.forward();
MoreTabDialog.show(context,
callBack: this,
animationController: _animationController);
},
),
)),
],
),
),
),
),
onWillPop: () {
int newTime = DateTime.now().millisecondsSinceEpoch;
int result = newTime - lastTime;
lastTime = newTime;
if (result > 2000) {
Fluttertoast.showToast(msg: "再按一次退出");
} else {
SystemNavigator.pop();
}
return null;
}),
);
}
void onPageChanged(int value) {
setState(() {
this._currentIndex = value;
});
}
void onTap(int value) {
setState(() {
this._currentIndex = value;
});
}
@override
void onEnergyReading() {
}
@override
void onEquipmentRepair() {
}
@override
void onScanCallBack() {
}
}
2.dialog
static show(BuildContext context,
{MoreTabDialogCallBack callBack, AnimationController animationController}) {
showModalBottomSheet(
//设置背景色,背景为透明
backgroundColor: ColorUtils.CLEAR,
context: context,
builder: (BuildContext context) {
return SafeArea(
child: AnimatedPadding(
padding: MediaQuery.of(context).viewInsets, //边距(必要)
duration: const Duration(milliseconds: 100), //时常 (必要)
child: Container(
//设置内容背景色,背景为透明
color: ColorUtils.CLEAR,
margin: new EdgeInsets.only(bottom: 13),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ScaleTransition(
scale: animationController,
alignment: Alignment.bottomCenter,
child: Container(
width: 5000,
height: 150,
margin: new EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(10.0),
),
color: Colors.white,
// image: DecorationImage(
// image: AssetImage("images/ic_work_bg.png"),
// fit: BoxFit.fill,
// ),
),
child: Flex(
direction: Axis.horizontal,
children: [
Expanded(
flex: 1,
child: GestureDetector(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"images/ic_more_dialog_scan.png",
width: 60,
height: 60,
),
Container(
margin: new EdgeInsets.only(top: 5),
child: Text(
"一",
style: TextStyle(
color: Colors.black, fontSize: 15),
),
)
],
),
onTap: () {
callBack.onScanCallBack();
},
)),
Expanded(
flex: 1,
child: GestureDetector(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"images/ic_equipment_repair.png",
width: 60,
height: 60,
),
Container(
margin: new EdgeInsets.only(top: 5),
child: Text(
"一",
style: TextStyle(
color: Colors.black, fontSize: 15),
),
)
],
),
onTap: () {
callBack.onEquipmentRepair();
},
)),
Expanded(
flex: 1,
child: GestureDetector(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"images/ic_energy_reading.png",
width: 60,
height: 60,
),
Container(
margin: new EdgeInsets.only(top: 5),
child: Text(
"一",
style: TextStyle(
color: Colors.black, fontSize: 15),
),
)
],
),
onTap: () {
callBack.onEnergyReading();
},
)),
],
),
),
),
ScaleTransition(
scale: animationController,
alignment: Alignment.center,
child: GestureDetector(
onTap: () {
Navigator.of(context).pop();
},
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
),
child: Icon(Icons.close, color: Colors.white,),
padding: new EdgeInsets.all(3),
),
),
),
],
),
),
),
);
},
);
}