Flutter中如何实现自定义底部导航栏
Flutter 自定义凸起底部导航栏实现教程

实现思路
这种中间凸起的底部导航栏不用引入第三方库,用Flutter自带组件即可实现,核心逻辑是:
- 用
Scaffold自带的floatingActionButton属性实现中间凸起按钮 - 搭配
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked让按钮嵌入底部导航栏中间位置 - 底部导航栏用
BottomAppBar配合Row排列左右两侧的导航项 - 用
IndexedStack或者PageView管理导航对应的页面切换,保留页面状态
完整可运行示例代码
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( title: '自定义底部导航', theme: ThemeData(primarySwatch: Colors.blue), home: const HomePage(), ); } } 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 [ HomeScreen(), CategoryScreen(), PublishScreen(), CartScreen(), MineScreen(), ]; // 导航项配置 final List<Map<String, dynamic>> _navItems = [ {'icon': Icons.home, 'label': '首页'}, {'icon': Icons.category, 'label': '分类'}, {'icon': null, 'label': '发布'}, // 中间凸起按钮单独处理 {'icon': Icons.shopping_cart, 'label': '购物车'}, {'icon': Icons.person, 'label': '我的'}, ]; @override Widget build(BuildContext context) { return Scaffold( body: IndexedStack( index: _currentIndex, children: _pages, ), // 底部导航栏 bottomNavigationBar: BottomAppBar( shape: const CircularNotchedRectangle(), // 中间凹口适配凸起按钮 notchMargin: 6, // 凹口和按钮的间距 child: Container( height: 60, padding: const EdgeInsets.symmetric(horizontal: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: List.generate(_navItems.length, (index) { // 中间位置留空给FloatingActionButton if (index == 2) { return const SizedBox(width: 40); } return GestureDetector( onTap: () { setState(() { _currentIndex = index; }); }, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( _navItems[index]['icon'], color: _currentIndex == index ? Colors.blue : Colors.grey, size: 24, ), Text( _navItems[index]['label'], style: TextStyle( color: _currentIndex == index ? Colors.blue : Colors.grey, fontSize: 12, ), ) ], ), ); }), ), ), ), // 中间凸起按钮 floatingActionButton: FloatingActionButton( backgroundColor: Colors.blue, shape: const CircleBorder(), onPressed: () { setState(() { _currentIndex = 2; }); // 如果不需要跳转到页面,这里直接写你要的逻辑,比如弹出发布窗口 }, child: const Icon(Icons.add, color: Colors.white, size: 30), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, ); } } // 示例页面,你可以替换成自己的页面实现 class HomeScreen extends StatelessWidget { const HomeScreen({super.key}); @override Widget build(BuildContext context) => const Center(child: Text('首页')); } class CategoryScreen extends StatelessWidget { const CategoryScreen({super.key}); @override Widget build(BuildContext context) => const Center(child: Text('分类')); } class PublishScreen extends StatelessWidget { const PublishScreen({super.key}); @override Widget build(BuildContext context) => const Center(child: Text('发布页')); } class CartScreen extends StatelessWidget { const CartScreen({super.key}); @override Widget build(BuildContext context) => const Center(child: Text('购物车')); } class MineScreen extends StatelessWidget { const MineScreen({super.key}); @override Widget build(BuildContext context) => const Center(child: Text('我的')); }
自定义调整说明
- 可以修改
BottomAppBar的color属性调整导航栏背景色,修改shape调整凹口样式 - 可以修改
FloatingActionButton的样式、图标、点击逻辑适配你的业务需求 - 如果需要页面切换动画,把
IndexedStack替换为PageView+PageController即可 - 导航项的图标、文字、选中色都可以根据你的设计稿调整
内容的提问来源于stack exchange,提问作者Shammah
相关产品推荐
相关产品推荐

