Flutter实现凹陷式底部导航栏时凹槽位置偏移问题求助
Flutter BottomAppBar圆形凹陷错位修复
问题现象
目标实现带中心圆形凹陷的悬浮底部导航效果,参考设计如下:
编写代码运行后出现凹陷位置与悬浮按钮错位的异常,效果如下:
原有问题代码:
class _HomePageState extends State<HomePage> { @override Widget build(BuildContext context) { final Size size = MediaQuery.of(context).size; return Scaffold( floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, floatingActionButton: FloatingActionButton( onPressed: () {}, child: Icon(Icons.add), elevation: 2.0, ), bottomNavigationBar: Padding( padding: const EdgeInsets.only(left: 32.0, right: 32.0, bottom: 32.0), child: BottomAppBar( child: Row( mainAxisSize: MainAxisSize.max, children: <Widget>[ Expanded( child: Image.asset('assets/images/account.png', width: 32.0, height: 32.0, fit: BoxFit.contain), ), Expanded( child: Image.asset('assets/images/settings.png', width: 32.0, height: 32.0, fit: BoxFit.contain), ) ], ), shape: CircularNotchedRectangle(), color: Colors.white, ), ), body: Center( child: Text('Home Page'), )); } }
错位原因
CircularNotchedRectangle是基于BottomAppBar自身占满的底部导航区域和FAB的相对位置计算凹陷位置的。在BottomAppBar外层包裹左右、底部边距,会压缩BottomAppBar自身的渲染范围,导致它计算FAB相对位置时出现偏差,凹陷和FAB自然错位。- BottomAppBar内部的Row用两个
Expanded均分全部宽度,没有给中心悬浮按钮预留空位,会导致两侧图标被凹陷区域遮挡。 - 直接给BottomAppBar加外层边距时,悬浮按钮的垂直位置没有同步适配,也会加重错位问题。
修复方案
- 不要在BottomAppBar外层直接加边距,将BottomAppBar本身设为透明、占满整个底部导航区域,边距、圆角、背景样式全部放到BottomAppBar内部的容器上,保证BottomAppBar能正确获取FAB的相对位置,让凹陷和FAB完全对齐。
- 在导航栏行布局的两个图标中间插入固定宽度的占位组件,预留出FAB和凹陷的位置,避免图标被遮挡。
- 给Scaffold设置
extendBody: true,让页面内容延伸到导航栏下方,实现设计图的悬浮通透效果,按需调整notchMargin参数控制凹陷和FAB的间距即可。
修复后可直接运行的代码
class _HomePageState extends State<HomePage> { @override Widget build(BuildContext context) { return Scaffold( extendBody: true, floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, floatingActionButton: FloatingActionButton( onPressed: () {}, child: const Icon(Icons.add), elevation: 2.0, ), bottomNavigationBar: BottomAppBar( color: Colors.transparent, elevation: 0, // 控制凹陷和FAB的间距 notchMargin: 8.0, clipBehavior: Clip.antiAlias, shape: const CircularNotchedRectangle(), child: Padding( // 原外层边距移到BottomAppBar内部,不影响位置计算 padding: const EdgeInsets.only(left: 32.0, right: 32.0, bottom: 32.0), child: Container( height: 64, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(24)), ), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ IconButton( icon: Image.asset('assets/images/account.png', width: 32.0, height: 32.0, fit: BoxFit.contain), onPressed: () {}, ), // 中间占位,预留FAB和凹陷位置 const SizedBox(width: 56), IconButton( icon: Image.asset('assets/images/settings.png', width: 32.0, height: 32.0, fit: BoxFit.contain), onPressed: () {}, ), ], ), ), ), ), body: const Center( child: Text('Home Page'), ), ); } }
提示:如果运行后凹陷和FAB的贴合度不符合预期,可以微调
notchMargin数值和中间占位SizedBox的宽度,直到和设计效果完全一致。
内容的提问来源于stack exchange,提问作者picture talks
相关产品推荐
相关产品推荐

