自定義形狀TabBar
實現
成員變量
List<Widget> pages = List<Widget>();用來存放tabs分別對應的頁面。
tabbar作為內容的容器 CustomerNotification用來容器內部Widget來通知tabbar做出改變。
NotificationListener的’ child‘是頁面顯示內容,NotificationListener用來監聽CustomerNotification。
看設計,TabBar是懸浮的設計,不能用官方庫提供的TabBar。TabBar和后面的內容是堆疊的,所以用Stack widget。
Stack: 取代線性布局,Stack允許子 widget 堆疊, 你可以使用 Positioned 來定位他們相對于Stack的上下左右四條邊的位置。Stacks是基于Web開發中的絕對定位(absolute positioning )布局模型設計的。
Positioned,用Positioned絕對定位懸浮的TabBar。 bottom: 15, left: 20, right: 20(top這里不需要設置)分別設計和容器的邊距。
Positioned的child就是你自己需要定制的tabbar形狀。
全部代碼如下:
/*
* File: /Users/ottpay/Git/fyh_transfer/lib/Account/accountHomePage.dart
* Created Date: 2019-09-27 4:13:15
* Author: 木穆 pengxuan.li@ott.ca
* Copyright (c) 2019 OTT Pay HK
*/
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fyh_transfer/Send/sendHomePage.dart';
import 'package:fyh_transfer/Order/orderHomePage.dart';
import 'package:fyh_transfer/Account/accountHomePage.dart';
class FYHTabbar extends StatefulWidget {
@override
FYHTabbarState createState() => FYHTabbarState();
}
class CustomerNotification extends Notification {
CustomerNotification(this.enable);
final bool enable;
}
class FYHTabbarState extends State<FYHTabbar>
{
List<Widget> pages = List<Widget>();
bool sendeEnable = false;
SendHomePage _sendHomePage = SendHomePage();
int _activeIndex = 1; //激活項
double _height = 48.0; //導航欄高度
double _floatRadius = 30; //懸浮圖標半徑
List _navs = [
Icons.search,
Icons.ondemand_video,
Icons.music_video,
]; //導航項
@override
void initState() {
super.initState();
pages..add(OrderHomePage())..add(_sendHomePage)..add(AccountHomePage());
}
@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width - 40;
_floatRadius = _activeIndex == 1 ? 40 : 30;
return NotificationListener<CustomerNotification>(
onNotification: (notification) {
setState(() {
sendeEnable = notification.enable;
});
},
child: Container(
child: Stack(children: [
pages[_activeIndex],//tabbar要存放的頁面
Positioned(//用Positioned絕對定位懸浮的TabBar
bottom: 15,
left: 20,
right: 20,
child: Container(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
width: width,
height: 58,
child: Stack(
overflow: Overflow.visible,
children: <Widget>[
//所有圖標
DecoratedBox(
decoration: BoxDecoration(
color: Color(0xFFf2f4f5), // 底色
shape: BoxShape.rectangle, // 默認值也是矩形
borderRadius:
new BorderRadius.circular(_height / 2), // 圓角度
boxShadow: [
BoxShadow(
color: Color(0xffc1c1c1),
offset: Offset(2.0, 2.0),
blurRadius: 50.0,
spreadRadius: 2.0)
],
),
child: SizedBox(
height: _height,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: _navs
.asMap()
.map((i, v) => MapEntry(
i,
GestureDetector(
child: Container(
color: Color(0xFFf2f4f5),
margin: EdgeInsets.all(0),
child: Icon(v,
color: _activeIndex == i
? Colors.pink
: Colors.grey),
width: width / 3 - 30,
height: _height - 10,
),
onTap: () {
_switchNav(i);
},
)))
.values
.toList(),
),
),
),
//浮動圖標
Positioned(
bottom: 4,
left: (width) / 2 - _floatRadius,
child: DecoratedBox(
decoration:
ShapeDecoration(shape: CircleBorder(), shadows: [
BoxShadow(
blurRadius: 2,
offset: Offset(0, 2),
spreadRadius: 0,
color: _activeIndex == 1
? Colors.pink
: Colors.grey),
]),
child: GestureDetector(
child: CircleAvatar(
radius: _floatRadius, //浮動圖標和圓弧之間設置8pixel間隙
backgroundColor: Color(0xfff2f4f5),
child: Icon(_navs[1],
color: sendeEnable && _activeIndex == 1
? Colors.pink
: Colors.black38)),
onTap: () {
_clickMiddleBtn();
},
),
),
),
],
),
),
)
]),
));
;
}
//中間按鈕
_clickMiddleBtn() {
if (_activeIndex == 1) {
if (sendeEnable) {
print('send');
}
} else {
_switchNav(1);
}
}
//切換導航
_switchNav(int newIndex) {
print(newIndex);
double oldPosition = _activeIndex.toDouble();
double newPosition = newIndex.toDouble();
if (oldPosition != newPosition) {
setState(() {
_activeIndex = newIndex;
});
}
}
@override
void dispose() {
super.dispose();
}
}