Flutter自定义底部导航栏实现指导(含现有代码优化需求)
自定义底部导航栏实现指导
我想要创建一个自定义底部导航栏,样式要求如下:
目标样式:底部导航栏中间有一个突出的圆形图标,两侧各分布两个带文字标签的导航图标,选中的图标和文字有高亮效果,导航栏整体带有圆角缺口适配中间图标。
需要自定义应用中底部导航栏的外观与功能,恳请提供实现指导、技巧、代码片段或推荐资源。
补充说明
当前我的实现效果如下:
当前效果:底部导航栏有圆角缺口,中间放置了SVG图标但位置略有偏移,普通图标仅显示图标无文字标签,未实现选中态高亮。
以下是我当前的代码:
Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Custom Bottom Navigation Bar'), ), body: Center( child: Text('Page ${_currentIndex + 1}'), ), bottomNavigationBar: Stack( children: <Widget>[ BottomAppBar( shape: CircularNotchedRectangle(), child: Container( height: 60.0, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ IconButton( icon: Icon(Icons.home), onPressed: () { setState(() { _currentIndex = 0; }); }, ), IconButton( icon: Icon(Icons.search), onPressed: () { setState(() { _currentIndex = 1; }); }, ), SizedBox(width: 80.0), // 为SVG图标预留空间 IconButton( icon: Icon(Icons.notifications), onPressed: () { setState(() { _currentIndex = 2; }); }, ), IconButton( icon: Icon(Icons.person), onPressed: () { setState(() { _currentIndex = 3; }); }, ), ], ), ), ), Positioned( top: -40.0, left: MediaQuery.of(context).size.width / 2 - 24.0, child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ SvgPicture.asset( 'assets/rugby.svg', // 替换为你的SVG文件路径 height: 80.0, width: 80.0, ), Text('Icône', style: TextStyle(fontSize: 12.0)), ], ), ), ], ), ); }
优化实现方案
1. 匹配目标样式的核心调整
- 选中态高亮:给选中的图标和文字添加颜色变化,底部增加高亮小色块标识
- 中间图标优化:修正位置确保完全居中,添加点击事件,给图标套白色背景+阴影增强立体感
- 文字标签补充:给底部普通导航图标添加对应文字标签,统一风格
- 布局细节调整:调整导航栏高度,优化间距让整体更贴合目标样式
2. 优化后的完整代码
int _currentIndex = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('自定义底部导航栏'), ), body: Center( child: _getCurrentPage(), ), bottomNavigationBar: Stack( alignment: Alignment.center, children: <Widget>[ BottomAppBar( elevation: 8, shape: const CircularNotchedRectangle(), color: Colors.white, child: SizedBox( height: 70, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ _buildNavItem(Icons.home, '首页', 0), _buildNavItem(Icons.search, '搜索', 1), const SizedBox(width: 80), // 为中间图标预留固定空间 _buildNavItem(Icons.notifications, '通知', 2), _buildNavItem(Icons.person, '我的', 3), ], ), ), ), Positioned( top: -30, child: GestureDetector( onTap: () { setState(() { _currentIndex = 4; // 用特殊索引标识中间页面 }); }, child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(40), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.3), blurRadius: 8, spreadRadius: 2, ) ], ), child: SvgPicture.asset( 'assets/rugby.svg', height: 60, width: 60, ), ), const SizedBox(height: 5), const Text('赛事', style: TextStyle(fontSize: 12)), ], ), ), ), ], ), ); } // 封装导航项,统一处理选中/未选中状态 Widget _buildNavItem(IconData icon, String label, int index) { bool isSelected = _currentIndex == index; return GestureDetector( onTap: () { setState(() { _currentIndex = index; }); }, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, color: isSelected ? Colors.blue : Colors.grey, size: 26, ), const SizedBox(height: 4), Text( label, style: TextStyle( color: isSelected ? Colors.blue : Colors.grey, fontSize: 12, fontWeight: isSelected ? FontWeight.w500 : FontWeight.normal, ), ), if (isSelected) Container( width: 20, height: 2, color: Colors.blue, margin: const EdgeInsets.only(top: 2), ), ], ), ); } // 根据当前索引返回对应页面 Widget _getCurrentPage() { switch (_currentIndex) { case 0: return const Text('首页'); case 1: return const Text('搜索页'); case 2: return const Text('通知页'); case 3: return const Text('个人中心'); case 4: return const Text('赛事页面'); default: return const Text('首页'); } }
3. 关键实现技巧
- 组件封装:用
_buildNavItem方法统一处理导航项的状态,减少重复代码,便于后续样式修改 - 立体效果:给中间图标添加白色背景和阴影,模拟悬浮突出的视觉效果
- 精准布局:通过
Stack的alignment: Alignment.center直接让中间图标居中,避免手动计算屏幕宽度的误差 - 状态扩展:如果应用复杂度较高,可替换
setState为Provider、GetX等状态管理工具,提升性能和可维护性 - 交互完善:给所有导航项添加点击事件,中间图标单独处理跳转逻辑,确保功能完整
内容的提问来源于stack exchange,提问作者MICHEL Baptiste
相关产品推荐
相关产品推荐

