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

如何实现选中的Bottom Navigation Item凸起于底部导航栏?

实现选中项凸起的底部导航栏

可以用自定义布局实现这个效果,完全不需要依赖第三方包,能避免导航栏偏移的问题,核心思路是通过Stack分层布局,让选中项单独向上凸起,同时保持底部导航栏的原有位置。

完整代码实现

自定义导航栏组件

import 'package:flutter/material.dart';

class CustomBottomNav extends StatefulWidget {
  final int selectedIndex;
  final Function(int) onTap;
  final List<BottomNavItemModel> items;

  const CustomBottomNav({
    super.key,
    required this.selectedIndex,
    required this.onTap,
    required this.items,
  });

  @override
  State<CustomBottomNav> createState() => _CustomBottomNavState();
}

class _CustomBottomNavState extends State<CustomBottomNav> {
  @override
  Widget build(BuildContext context) {
    // 给凸起项预留额外空间,避免超出屏幕
    final navHeight = kBottomNavigationBarHeight + 20;
    return Container(
      height: navHeight,
      color: Colors.white,
      child: Stack(
        alignment: Alignment.bottomCenter,
        children: [
          // 底部导航栏的基础背景层(带阴影,模拟原生导航栏效果)
          Positioned(
            bottom: 0,
            left: 0,
            right: 0,
            height: kBottomNavigationBarHeight,
            child: Container(
              decoration: BoxDecoration(
                color: Colors.white,
                boxShadow: [
                  BoxShadow(
                    color: Colors.grey.withOpacity(0.2),
                    blurRadius: 10,
                    offset: const Offset(0, -2),
                  ),
                ],
              ),
            ),
          ),
          // 导航项列表层
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: List.generate(widget.items.length, (index) {
              final isSelected = index == widget.selectedIndex;
              return GestureDetector(
                onTap: () => widget.onTap(index),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    if (isSelected)
                      // 选中项的凸起圆形背景 + 图标
                      Container(
                        width: 60,
                        height: 60,
                        margin: const EdgeInsets.only(bottom: 5),
                        decoration: const BoxDecoration(
                          color: Colors.blue,
                          shape: BoxShape.circle,
                          boxShadow: [
                            BoxShadow(
                              color: Colors.blue.withOpacity(0.3),
                              blurRadius: 10,
                              offset: Offset(0, -2),
                            ),
                          ],
                        ),
                        child: Center(
                          child: Icon(
                            widget.items[index].icon,
                            color: Colors.white,
                            size: 28,
                          ),
                        ),
                      ),
                    if (!isSelected)
                      // 未选中项的普通图标
                      Icon(
                        widget.items[index].icon,
                        color: Colors.grey,
                        size: 24,
                      ),
                    const SizedBox(height: 4),
                    // 导航项文字
                    Text(
                      widget.items[index].label,
                      style: TextStyle(
                        color: isSelected ? Colors.blue : Colors.grey,
                        fontSize: 12,
                        fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
                      ),
                    ),
                  ],
                ),
              );
            }),
          ),
        ],
      ),
    );
  }
}

// 导航项数据模型,方便复用
class BottomNavItemModel {
  final IconData icon;
  final String label;

  BottomNavItemModel({required this.icon, required this.label});
}

使用示例

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // 默认选中中间项,和示例图效果匹配
  int _selectedIndex = 2;

  final List<BottomNavItemModel> _navItems = [
    BottomNavItemModel(icon: Icons.home, label: "首页"),
    BottomNavItemModel(icon: Icons.search, label: "搜索"),
    BottomNavItemModel(icon: Icons.add, label: "发布"),
    BottomNavItemModel(icon: Icons.message, label: "消息"),
    BottomNavItemModel(icon: Icons.person, label: "我的"),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: const Center(child: Text("主内容区")),
      bottomNavigationBar: CustomBottomNav(
        selectedIndex: _selectedIndex,
        onTap: (index) => setState(() => _selectedIndex = index),
        items: _navItems,
      ),
    );
  }
}

关键实现细节

  • 分层布局:用Stack把导航栏背景和导航项分开,背景层保持原生底部导航栏高度,不会偏移;选中项放在上层,通过margin向上凸起。
  • 预留空间:给容器设置kBottomNavigationBarHeight + 20的高度,确保凸起的项不会超出屏幕底部,也不会挤压主内容区。
  • 样式匹配:选中项添加圆形背景和阴影,和你提供的示例图效果一致,未选中项保持普通样式。

内容的提问来源于stack exchange,提问作者Arbaz Irshad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 01:42:45