如何在Flutter中实现指定样式的自定义底部导航栏?
实现方案
你当前手动用Row实现底部导航栏的方式,不仅适配性差,也很难实现示例中中间按钮凸起的效果,推荐用Flutter官方提供的BottomAppBar + FloatingActionButton组合来实现,适配和效果都能得到保证,修正后的代码参考如下:
import 'package:flutter/material.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { // 记录当前选中的导航索引 int _currentIndex = 0; // 对应索引的页面,按需替换成你自己的页面 final List<Widget> _pages = const [ Center(child: Text('我的页面')), Center(child: Text('选举页面')), Center(child: Text('添加选举页面')), Center(child: Text('客户页面')), ]; @override Widget build(BuildContext context) { return Scaffold( // 主体页面内容 body: _pages[_currentIndex], // 中间凸起的扫描按钮 floatingActionButton: FloatingActionButton( onPressed: () { // 这里写点击扫描按钮的逻辑 Navigator.push(context, MaterialPageRoute(builder: (context) => const ScanPage())); }, backgroundColor: const Color(0xfffed307), shape: const CircleBorder(), child: Image.asset( 'images/scan_icon.png', height: 24, width: 24, ), ), // 悬浮按钮的位置:底部中间 floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, // 底部导航栏 bottomNavigationBar: BottomAppBar( color: const Color(0xfffed307), // 底部栏的凹口形状,适配中间的悬浮按钮 shape: const CircularNotchedRectangle(), // 凹口和悬浮按钮的间距 notchMargin: 8, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ // 第一个导航项:我的页面 IconButton( onPressed: () { setState(() { _currentIndex = 0; }); }, icon: Icon( Icons.store_mall_directory_outlined, // 选中时颜色变黑,未选中变灰,按需调整 color: _currentIndex == 0 ? Colors.black : Colors.grey, ), ), // 第二个导航项:选举 IconButton( onPressed: () { setState(() { _currentIndex = 1; }); }, icon: Icon( Icons.apps, color: _currentIndex == 1 ? Colors.black : Colors.grey, ), ), // 这里留空放中间的悬浮按钮 const SizedBox(width: 40), // 第三个导航项:添加选举 IconButton( onPressed: () { setState(() { _currentIndex = 2; }); }, icon: Icon( Icons.apps, color: _currentIndex == 2 ? Colors.black : Colors.grey, ), ), // 第四个导航项:客户 IconButton( onPressed: () { setState(() { _currentIndex = 3; }); }, icon: Icon( Icons.groups_outlined, color: _currentIndex == 3 ? Colors.black : Colors.grey, ), ), ], ), ), ); } } // 扫描页面占位,按需替换成你自己的实现 class ScanPage extends StatelessWidget { const ScanPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('扫描')), body: const Center(child: Text('扫描功能页面')), ); } }
注意事项
- 如果要给导航项加底部文字,可以把IconButton换成Column组件,里面放Icon和Text即可,记得根据选中状态调整文字颜色
- 所有图标如果Material自带的不符合需求,都可以替换成
Image.asset引入你自己的本地图标资源 - 代码里的页面逻辑和颜色样式都可以根据你的实际需求调整
内容的提问来源于stack exchange,提问作者Siddharth Mehra
相关产品推荐
相关产品推荐

