You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何不使用第三方包在Flutter中实现指定样式的自定义底部导航栏

实现自定义凸起样式底部导航栏(无第三方依赖)

本方案实现的是底部导航栏中间带凸起悬浮按钮的效果,全程使用Flutter官方组件,无需引入任何第三方依赖。

实现步骤

  • 整体使用Scaffold组件的bottomNavigationBar属性承载底部导航主体,floatingActionButton属性承载中间凸起的按钮,搭配floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked实现按钮嵌入底部导航的凹槽效果。
  • 自定义底部导航栏主体,使用BottomAppBar组件作为容器,设置shape: CircularNotchedRectangle()实现适配中间按钮的凹槽剪裁效果。
  • 在BottomAppBar内部使用Row布局排列两侧的导航项,左右各留空一个位置给中间的悬浮按钮占位,导航项包含图标和文字,根据选中状态切换样式。
  • 定义int _currentIndex变量存储当前选中的导航索引,点击导航项或者中间按钮时更新索引,配合IndexedStack实现对应页面的切换,保证页面状态不会丢失。
  • 处理选中态逻辑,导航项点击时触发setState更新_currentIndex,对应项的图标和文字设置高亮颜色,未选中项置灰。

核心代码逻辑

1. 主页面框架代码

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(), // 中间凸起按钮对应页面
    MessageScreen(),
    MineScreen(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(
        index: _currentIndex,
        children: _pages,
      ),
      // 中间凸起按钮
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => _currentIndex = 2),
        backgroundColor: _currentIndex == 2 ? Colors.blue : Colors.grey,
        child: const Icon(Icons.add, color: Colors.white),
      ),
      // 悬浮按钮位置:居中嵌入底部导航
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      // 底部导航主体
      bottomNavigationBar: BottomAppBar(
        shape: const CircularNotchedRectangle(), // 适配凸起按钮的凹槽形状
        notchMargin: 6, // 按钮和凹槽的留白间距
        child: SizedBox(
          height: 60,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              // 左侧第一个导航项
              _buildNavItem(0, Icons.home, "首页"),
              _buildNavItem(1, Icons.category, "分类"),
              // 中间占位,给凸起按钮留空间
              const SizedBox(width: 40),
              // 右侧导航项
              _buildNavItem(3, Icons.message, "消息"),
              _buildNavItem(4, Icons.person, "我的"),
            ],
          ),
        ),
      ),
    );
  }

  // 封装单个导航项组件
  Widget _buildNavItem(int index, IconData icon, String label) {
    return GestureDetector(
      onTap: () => setState(() => _currentIndex = index),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(
            icon,
            color: _currentIndex == index ? Colors.blue : Colors.grey,
          ),
          Text(
            label,
            style: TextStyle(
              color: _currentIndex == index ? Colors.blue : Colors.grey,
              fontSize: 12,
            ),
          )
        ],
      ),
    );
  }
}

2. 占位页面示例代码(可根据业务自行替换)

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 MessageScreen extends StatelessWidget {const MessageScreen({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("我的"));}

自定义调整说明

  • 如果需要修改凹槽的大小或者形状,可以自定义ShapeBorder替换CircularNotchedRectangle即可
  • 可以修改FloatingActionButton的样式实现不同的凸起按钮效果,比如方形、带边框、自定义图标等
  • 导航项的选中态样式、图标、文字都可以根据设计稿自行调整参数

内容的提问来源于stack exchange,提问作者Ahmed Sabahy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 01:06:01