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

如何在Flutter中实现指定样式的自定义底部导航栏?

实现方案

你当前手动用Row实现底部导航栏的方式,不仅适配性差,也很难实现示例中中间按钮凸起的效果,推荐用Flutter官方提供的BottomAppBar + FloatingActionButton组合来实现,适配和效果都能得到保证,修正后的代码参考如下:

import 'package:flutter/material.dart';

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 [
    Center(child: Text('我的页面')),
    Center(child: Text('选举页面')),
    Center(child: Text('添加选举页面')),
    Center(child: Text('客户页面')),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 主体页面内容
      body: _pages[_currentIndex],
      // 中间凸起的扫描按钮
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 这里写点击扫描按钮的逻辑
          Navigator.push(context, MaterialPageRoute(builder: (context) => const ScanPage()));
        },
        backgroundColor: const Color(0xfffed307),
        shape: const CircleBorder(),
        child: Image.asset(
          'images/scan_icon.png',
          height: 24,
          width: 24,
        ),
      ),
      // 悬浮按钮的位置:底部中间
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      // 底部导航栏
      bottomNavigationBar: BottomAppBar(
        color: const Color(0xfffed307),
        // 底部栏的凹口形状,适配中间的悬浮按钮
        shape: const CircularNotchedRectangle(),
        // 凹口和悬浮按钮的间距
        notchMargin: 8,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            // 第一个导航项:我的页面
            IconButton(
              onPressed: () {
                setState(() {
                  _currentIndex = 0;
                });
              },
              icon: Icon(
                Icons.store_mall_directory_outlined,
                // 选中时颜色变黑,未选中变灰,按需调整
                color: _currentIndex == 0 ? Colors.black : Colors.grey,
              ),
            ),
            // 第二个导航项:选举
            IconButton(
              onPressed: () {
                setState(() {
                  _currentIndex = 1;
                });
              },
              icon: Icon(
                Icons.apps,
                color: _currentIndex == 1 ? Colors.black : Colors.grey,
              ),
            ),
            // 这里留空放中间的悬浮按钮
            const SizedBox(width: 40),
            // 第三个导航项:添加选举
            IconButton(
              onPressed: () {
                setState(() {
                  _currentIndex = 2;
                });
              },
              icon: Icon(
                Icons.apps,
                color: _currentIndex == 2 ? Colors.black : Colors.grey,
              ),
            ),
            // 第四个导航项:客户
            IconButton(
              onPressed: () {
                setState(() {
                  _currentIndex = 3;
                });
              },
              icon: Icon(
                Icons.groups_outlined,
                color: _currentIndex == 3 ? Colors.black : Colors.grey,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// 扫描页面占位,按需替换成你自己的实现
class ScanPage extends StatelessWidget {
  const ScanPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('扫描')),
      body: const Center(child: Text('扫描功能页面')),
    );
  }
}

注意事项

  • 如果要给导航项加底部文字,可以把IconButton换成Column组件,里面放Icon和Text即可,记得根据选中状态调整文字颜色
  • 所有图标如果Material自带的不符合需求,都可以替换成Image.asset引入你自己的本地图标资源
  • 代码里的页面逻辑和颜色样式都可以根据你的实际需求调整

内容的提问来源于stack exchange,提问作者Siddharth Mehra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 03:45:03