Flutter自定义形状底部导航栏实现技术求助
Flutter 自定义凸起式底部导航栏实现方案
根据你提供的设计需求,这里给出一套可直接复用的实现方案,核心是通过自定义绘制背景+堆叠导航项来实现中间凸起的效果。
核心实现思路
- 用
CustomPainter绘制带中间圆弧凹陷的导航栏背景 - 用
Stack组件将自定义背景、普通导航项和中间凸起按钮分层叠加 - 利用
Scaffold的extendBody属性让页面内容延伸至导航栏下方,优化视觉体验
完整代码示例
1. 自定义背景绘制类
import 'package:flutter/material.dart'; class CustomNavBarPainter extends CustomPainter { final Color backgroundColor; final double notchRadius; final double notchHeight; CustomNavBarPainter({ required this.backgroundColor, this.notchRadius = 30, this.notchHeight = 20, }); @override void paint(Canvas canvas, Size size) { final paint = Paint()..color = backgroundColor; final path = Path(); // 左侧直线 path.lineTo(size.width / 2 - notchRadius, 0); // 左侧圆弧(形成凹陷) path.arcTo( Rect.fromCircle( center: Offset(size.width / 2, notchHeight), radius: notchRadius, ), pi, -pi, false, ); // 右侧直线 path.lineTo(size.width, 0); // 底部闭合路径 path.lineTo(size.width, size.height); path.lineTo(0, size.height); path.close(); canvas.drawPath(path, paint); } @override bool shouldRepaint(covariant CustomNavBarPainter oldDelegate) { return oldDelegate.backgroundColor != backgroundColor || oldDelegate.notchRadius != notchRadius || oldDelegate.notchHeight != notchHeight; } }
2. 页面与导航栏整合
class CustomBottomNavBarPage extends StatefulWidget { const CustomBottomNavBarPage({super.key}); @override State<CustomBottomNavBarPage> createState() => _CustomBottomNavBarPageState(); } class _CustomBottomNavBarPageState extends State<CustomBottomNavBarPage> { int _selectedIndex = 0; void _onItemTapped(int index) { setState(() { _selectedIndex = index; }); } @override Widget build(BuildContext context) { // 导航栏总高度,预留凸起空间 final navBarHeight = kBottomNavigationBarHeight + 10; return Scaffold( // 让页面内容延伸至导航栏下方,避免凹陷处留白 extendBody: true, body: Center( child: Text('当前选中页面索引: $_selectedIndex'), ), bottomNavigationBar: SizedBox( height: navBarHeight, child: Stack( alignment: Alignment.topCenter, children: [ // 绘制自定义背景 CustomPaint( size: Size(MediaQuery.of(context).size.width, navBarHeight), painter: CustomNavBarPainter( backgroundColor: Colors.white, notchRadius: 35, notchHeight: 25, ), ), // 普通导航项(中间位置留空给凸起按钮) Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildNavItem(Icons.home, 0), _buildNavItem(Icons.search, 1), const SizedBox(width: 70), // 中间占位,匹配凸起按钮宽度 _buildNavItem(Icons.favorite, 2), _buildNavItem(Icons.person, 3), ], ), // 中间凸起的导航按钮 Positioned( top: -20, // 向上偏移实现悬浮凸起效果 child: InkWell( onTap: () => _onItemTapped(4), borderRadius: BorderRadius.circular(35), child: Container( width: 70, height: 70, decoration: const BoxDecoration( color: Colors.blue, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black12, blurRadius: 8, spreadRadius: 2, ), ], ), child: const Icon( Icons.add, color: Colors.white, size: 32, ), ), ), ), ], ), ), ); } // 封装普通导航项 Widget _buildNavItem(IconData icon, int index) { final isSelected = _selectedIndex == index; return InkWell( onTap: () => _onItemTapped(index), child: Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Icon( icon, color: isSelected ? Colors.blue : Colors.grey, size: 28, ), ), ); } }
可调参数说明
- 修改
CustomNavBarPainter的notchRadius和notchHeight可以调整中间凹陷的大小和深度 - 调整凸起按钮的
top偏移量、宽高和颜色,适配你的设计风格 - 普通导航项的图标、选中态颜色都可以自定义修改
内容的提问来源于stack exchange,提问作者Hudaif
相关产品推荐
相关产品推荐

