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

如何在Flutter中实现图中这类底部导航栏?求更优方案

Flutter 实现带凸起中间按钮的底部导航栏(附优化方案)

从你提供的图来看,这是一种带凸起主操作按钮的底部导航栏,常见于社交、电商类APP,下面直接给出实现方案和优化技巧:

一、基础实现(官方组件组合,兼容性拉满)

用Flutter官方的BottomAppBar+FloatingActionButton实现,自带Material Design交互效果,不用自己写太多自定义逻辑:

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(
      home: const BottomNavWithFab(),
    );
  }
}

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

  @override
  State<BottomNavWithFab> createState() => _BottomNavWithFabState();
}

class _BottomNavWithFabState extends State<BottomNavWithFab> {
  int _selectedIndex = 0;
  // 对应导航栏的页面集合
  static const List<Widget> _pages = <Widget>[
    Center(child: Text('首页')),
    Center(child: Text('消息')),
    Center(child: Text('发布')),
    Center(child: Text('我的')),
    Center(child: Text('设置')),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _pages[_selectedIndex],
      // 底部导航栏容器,中间留凹口适配凸起按钮
      bottomNavigationBar: BottomAppBar(
        shape: const CircularNotchedRectangle(),
        notchMargin: 6.0,
        child: SizedBox(
          height: 60,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: <Widget>[
              IconButton(
                icon: Icon(
                  Icons.home,
                  color: _selectedIndex == 0 ? Colors.blue : Colors.grey,
                ),
                onPressed: () => _onItemTapped(0),
              ),
              IconButton(
                icon: Icon(
                  Icons.message,
                  color: _selectedIndex == 1 ? Colors.blue : Colors.grey,
                ),
                onPressed: () => _onItemTapped(1),
              ),
              // 中间留空给凸起按钮
              const SizedBox(width: 40),
              IconButton(
                icon: Icon(
                  Icons.person,
                  color: _selectedIndex == 3 ? Colors.blue : Colors.grey,
                ),
                onPressed: () => _onItemTapped(3),
              ),
              IconButton(
                icon: Icon(
                  Icons.settings,
                  color: _selectedIndex == 4 ? Colors.blue : Colors.grey,
                ),
                onPressed: () => _onItemTapped(4),
              ),
            ],
          ),
        ),
      ),
      // 中间凸起的主操作按钮
      floatingActionButton: FloatingActionButton(
        onPressed: () => _onItemTapped(2),
        child: const Icon(Icons.add),
        backgroundColor: _selectedIndex == 2 ? Colors.blue : Colors.orange,
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
    );
  }
}

二、优化方案(让导航栏更贴合设计+体验更好)

1. 自定义凸起按钮样式

如果官方Fab不符合设计风格,直接用自定义容器做凸起按钮,放在Stack里:

// 替换原Scaffold的floatingActionButton部分,同时把BottomAppBar换成普通Container
bottomNavigationBar: Container(
  height: 60,
  decoration: BoxDecoration(
    color: Colors.white,
    boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.2), blurRadius: 5)],
  ),
  child: Row(
    mainAxisAlignment: MainAxisAlignment.spaceAround,
    children: <Widget>[
      // 其他导航按钮和之前一样
    ],
  ),
),
body: Stack(
  children: [
    _pages[_selectedIndex],
    // 自定义凸起按钮
    Positioned(
      bottom: 10,
      left: MediaQuery.of(context).size.width / 2 - 30,
      child: GestureDetector(
        onTap: () => _onItemTapped(2),
        child: Container(
          width: 60,
          height: 60,
          decoration: BoxDecoration(
            color: _selectedIndex == 2 ? Colors.blue : Colors.orange,
            borderRadius: BorderRadius.circular(30),
            boxShadow: [
              BoxShadow(
                color: Colors.grey.withOpacity(0.3),
                spreadRadius: 2,
                blurRadius: 5,
                offset: const Offset(0, 3),
              )
            ],
          ),
          child: const Icon(Icons.add, color: Colors.white, size: 30),
        ),
      ),
    ),
  ],
)

2. 加页面切换动画

给页面切换加淡入淡出效果,提升流畅感:

// 替换原body的_pages[_selectedIndex]
AnimatedSwitcher(
  duration: const Duration(milliseconds: 300),
  child: _pages[_selectedIndex],
  transitionBuilder: (child, animation) {
    return FadeTransition(opacity: animation, child: child);
  },
)

3. 防重复点击

避免快速点击导致页面反复切换,加个简单的防抖:

bool _isClickable = true;

void _onItemTapped(int index) {
  if (!_isClickable) return;
  setState(() {
    _isClickable = false;
    _selectedIndex = index;
  });
  Future.delayed(const Duration(milliseconds: 200), () {
    setState(() {
      _isClickable = true;
    });
  });
}

4. 适配深色模式

让导航栏颜色随系统主题自动切换:

BottomAppBar(
  color: Theme.of(context).bottomAppBarColor,
  // 其他属性不变
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 04:54:52