/ 生活中常部分app底部导航样式 /
从智能手机的普及我们的生活 , 手机已经为我们生活提供了方便 . 购买手机后系统默认安装了很多款app , 随便点开一款手机app 进入应用首页映入眼帘的就是底部导航 . 可以说手机app为我们生活提供了方便 , 同时app里面的底部导航为我们更好的使用应用 . 市面上的app底部导航切换样式都差不多 (分底部Tab 导航栏 和 舵式导航栏) . 点击底部导航选项可以切换到不同功能模块的 , 让用户在使用的时候更加清楚要导航的功能 .
底部Tab(今日头条&微信&美团&网易云音乐)
舵式导航栏(微博&闲鱼)
/ app底部导航构造 /
导航选项纯图标
导航纯文本
** 导航图文混排**
/ Flutter 实现底部导航 /
BottomNavigationBarItem
服务于 BottomNavigationBar . 包括属性icon (选项图标)、title (选项包括文本的widget被弃用)、label (导航栏文本值)、backgroundColor、tooltip (长按时选项顶部弹出提示 , 长按选项时选项样式不会发生改变)
在flutter sdk 大于1.19.0的版本上使用该参数 , 抛出如下异常 :
backgroundColor 选项有渐变和移动效果状态下的背景颜色
BottomNavigationBar 参数 BottomNavigationBarType 是 BottomNavigationBarType.shifting 生效. 会覆盖导航栏BottomNavigationBar背景颜色 . 访问B站查看效果
tooltip 长按选项弹出提示 访问B站查看效果
BottomNavigationBar
底部导航 可以自定义 选项文本、图标样式 . 长按点击弹出提示 . 可以使用flutter自带的icon 切换图标的样式 .
items / BottomNavigationBarItem 集合
flutter自带的Icon
class StartPage extends StatefulWidget {
StartPage({Key? key, this.title}) : super(key: key);
final String? title;
@override
_StartPageState createState() => _StartPageState();
}
class _StartPageState extends State<StartPage> {
GlobalKey<StackWState> _stackGk = GlobalKey<StackWState>();
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BotNavBar(
stackValue: (int currentIndex, List<int> tabInt) {
_stackGk.currentState!.changeStack(currentIndex, tabInt);
},
),
body: StackWidget(
key: _stackGk,
));
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter_bottom_navigator/base/presenter/IPresenter.dart';
import 'package:flutter_bottom_navigator/base/view/BaseView.dart';
import 'package:flutter_bottom_navigator/base/view/IView.dart';
class StackWidget extends BaseView {
final int? currentIndex;
final List<int>? tabInt;
StackWidget({this.currentIndex, this.tabInt, Key? key}) : super(key: key);
@override
BaseViewState<IPresenter<IView>, BaseView> getState() {
return StackWState();
}
}
class StackWState extends BaseViewState<IPresenter, StackWidget> {
int _currentIndex = 0;
List<int>? _tabInt;
final List<Widget> _children = [
Center(
child: Text('Page1'),
),
Center(
child: Text('Page2'),
),
];
@override
void initState() {
// TODO: implement initState
super.initState();
_tabInt = widget.tabInt == null ? [0] : widget.tabInt;
}
void changeStack(int currentIndex, List<int> tabInt) {
setState(() {});
_currentIndex = currentIndex;
_tabInt = tabInt;
}
//Page的显示和隐藏
_child(int _index) {
return Offstage(
offstage: !(_currentIndex == _index),
child: _tabInt!.contains(_index) ? _children[_index] : Container(),
);
}
@override
buildWidget() {
// TODO: implement buildWidget
return Stack(
children: <Widget>[
_child(0),
_child(1),
],
);
}
}
import 'package:flutter/material.dart';
class BotNavBar extends StatefulWidget {
final ValueChanged? stackValue;
BotNavBar({Key? key, this.stackValue}) : super(key: key);
@override
_BotNavBarState createState() => _BotNavBarState();
}
class _BotNavBarState extends State<BotNavBar> {
List<int> tabInt = [0];
int _currentIndex = 0;
late var _tabImages;
@override
void initState() {
// TODO: implement initState
super.initState();
_tabImages = [
_singleTabImage('home'),
_singleTabImage('type'),
_singleTabImage('mine'),
];
}
BottomNavigationBarItem _singleBotNavItem(_index) {
return BottomNavigationBarItem(
icon: _getTabIcon(
_index,
),
backgroundColor: Colors.red,
tooltip: '$_index tooltips',
//title: Text('$_index title'),
label: '$_index label');
}
_singleTabImage(_labelPng) {
return [
Icon(Icons.message),
Icon(Icons.message),
];
}
Widget _getTabIcon(int curIndex) {
if (curIndex == _currentIndex) {
return _tabImages[curIndex][0];
}
return _tabImages[curIndex][1];
}
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: _onTabTapped,
currentIndex: _currentIndex,
backgroundColor: Colors.white,
selectedItemColor: Colors.blue,
unselectedItemColor: Color(0xffAFB1BD),
elevation: 6.0,
selectedIconTheme: IconThemeData(color: Colors.green),
unselectedIconTheme: IconThemeData(color: Colors.red),
selectedLabelStyle: TextStyle(inherit: true),
//fixedColor: Colors.blue,
showUnselectedLabels: true,
showSelectedLabels: true,
mouseCursor: SystemMouseCursors.move,
enableFeedback: false,
items: [
_singleBotNavItem(0),
_singleBotNavItem(1),
],
);
}
_onTabTapped(int index) {
setState(() {});
_currentIndex = index;
if (!tabInt.contains(index)) {
tabInt.add(index);
}
if (widget.stackValue != null) {
widget.stackValue!(_currentIndex, tabInt);
}
}
}
typedef ValueChanged = void Function(
int currentIndex,
List<int> tabInt,
);
自定义Icon
创建assets 文件夹来存储要读取的自定义图标
在 pubspec.yaml 文件里面配置图片的依赖
_singleTabImage(_labelPng) {
return [
Image.asset(
'assets/${_labelPng}_b_select.png',
width: 40.0,
height: 40.0,
),
Image.asset(
'assets/${_labelPng}_b_normal.png',
width: 40.0,
height: 40.0,
)
];
}
elevation / 导航栏阴影Z坐标
selectedLabelStyle / unselectedLabelStyle
导航栏选中文本的样式 (字体颜色、背景颜色、字体权重、字体阴影 、…...)
导航栏选中文本的样式 (字体颜色、背景颜色、字体权重、字体阴影 、……)
showSelectedLabels / showUnselectedLabels
是否显示选中时的文本 / 是否显示未选中时的文本
GlobalKey 实现导航局部刷新
StartPage 包含了 内容区域 (StackWidget) 和底部导航 (BotNavBar) . 当首次进入应用时 , 会渲染内容容区域和底部导航 . 当我们需要切换其它导航时 , 要做到切换导航重新渲染内容区域和底部导航而不执行内容区域和底部导航的父控件StartPage的build函数,需要用到GlobalKey来实现点击导航刷新内容区域而不执行StartPage的build函数 .
class _StartPageState extends State<StartPage> {
GlobalKey<StackWState> _stackGk = GlobalKey<StackWState>();
@override
Widget build(BuildContext context) {
print('StartPage _build');
return Scaffold(
bottomNavigationBar: BotNavBar(
stackValue: (int currentIndex, List<int> tabInt) {
_stackGk.currentState!.changeStack(currentIndex, tabInt);
},
),
body: StackWidget(
key: _stackGk,
));
}
}
flutter_bottom_navigator 案例下载
版权归原作者 xmiaoshen 所有, 如有侵权,请联系我们删除。