如何在Flutter中实现图中这类底部导航栏?求更优方案
Flutter 实现带凸起中间按钮的底部导航栏(附优化方案)
从你提供的图来看,这是一种带凸起主操作按钮的底部导航栏,常见于社交、电商类APP,下面直接给出实现方案和优化技巧:
一、基础实现(官方组件组合,兼容性拉满)
用Flutter官方的BottomAppBar+FloatingActionButton实现,自带Material Design交互效果,不用自己写太多自定义逻辑:
import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: const BottomNavWithFab(), ); } } class BottomNavWithFab extends StatefulWidget { const BottomNavWithFab({super.key}); @override State<BottomNavWithFab> createState() => _BottomNavWithFabState(); } class _BottomNavWithFabState extends State<BottomNavWithFab> { int _selectedIndex = 0; // 对应导航栏的页面集合 static const List<Widget> _pages = <Widget>[ Center(child: Text('首页')), Center(child: Text('消息')), Center(child: Text('发布')), Center(child: Text('我的')), Center(child: Text('设置')), ]; void _onItemTapped(int index) { setState(() { _selectedIndex = index; }); } @override Widget build(BuildContext context) { return Scaffold( body: _pages[_selectedIndex], // 底部导航栏容器,中间留凹口适配凸起按钮 bottomNavigationBar: BottomAppBar( shape: const CircularNotchedRectangle(), notchMargin: 6.0, child: SizedBox( height: 60, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ IconButton( icon: Icon( Icons.home, color: _selectedIndex == 0 ? Colors.blue : Colors.grey, ), onPressed: () => _onItemTapped(0), ), IconButton( icon: Icon( Icons.message, color: _selectedIndex == 1 ? Colors.blue : Colors.grey, ), onPressed: () => _onItemTapped(1), ), // 中间留空给凸起按钮 const SizedBox(width: 40), IconButton( icon: Icon( Icons.person, color: _selectedIndex == 3 ? Colors.blue : Colors.grey, ), onPressed: () => _onItemTapped(3), ), IconButton( icon: Icon( Icons.settings, color: _selectedIndex == 4 ? Colors.blue : Colors.grey, ), onPressed: () => _onItemTapped(4), ), ], ), ), ), // 中间凸起的主操作按钮 floatingActionButton: FloatingActionButton( onPressed: () => _onItemTapped(2), child: const Icon(Icons.add), backgroundColor: _selectedIndex == 2 ? Colors.blue : Colors.orange, ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, ); } }
二、优化方案(让导航栏更贴合设计+体验更好)
1. 自定义凸起按钮样式
如果官方Fab不符合设计风格,直接用自定义容器做凸起按钮,放在Stack里:
// 替换原Scaffold的floatingActionButton部分,同时把BottomAppBar换成普通Container bottomNavigationBar: Container( height: 60, decoration: BoxDecoration( color: Colors.white, boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.2), blurRadius: 5)], ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ // 其他导航按钮和之前一样 ], ), ), body: Stack( children: [ _pages[_selectedIndex], // 自定义凸起按钮 Positioned( bottom: 10, left: MediaQuery.of(context).size.width / 2 - 30, child: GestureDetector( onTap: () => _onItemTapped(2), child: Container( width: 60, height: 60, decoration: BoxDecoration( color: _selectedIndex == 2 ? Colors.blue : Colors.orange, borderRadius: BorderRadius.circular(30), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.3), spreadRadius: 2, blurRadius: 5, offset: const Offset(0, 3), ) ], ), child: const Icon(Icons.add, color: Colors.white, size: 30), ), ), ), ], )
2. 加页面切换动画
给页面切换加淡入淡出效果,提升流畅感:
// 替换原body的_pages[_selectedIndex] AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: _pages[_selectedIndex], transitionBuilder: (child, animation) { return FadeTransition(opacity: animation, child: child); }, )
3. 防重复点击
避免快速点击导致页面反复切换,加个简单的防抖:
bool _isClickable = true; void _onItemTapped(int index) { if (!_isClickable) return; setState(() { _isClickable = false; _selectedIndex = index; }); Future.delayed(const Duration(milliseconds: 200), () { setState(() { _isClickable = true; }); }); }
4. 适配深色模式
让导航栏颜色随系统主题自动切换:
BottomAppBar( color: Theme.of(context).bottomAppBarColor, // 其他属性不变 )
内容的提问来源于stack exchange,提问作者xihadulislam
相关产品推荐
相关产品推荐

