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

Flutter BottomNavigationBar仿iPhone Dock样式实现求助

问题描述

当前底部导航栏:白色背景、顶部圆角、带阴影的常规底部导航

想要实现类似iPhone Dock的底部导航栏:半透明模糊背景、顶部圆角、左右带边距的悬浮式导航栏

我正在开发Flutter应用,已实现带自定义AppBar的BottomNavigationBar,以下是HomePage组件代码:

import 'package:flutter/material.dart';
import 'dart:ui'; // For ImageFilter.blur

import 'screens/profile.dart';
import 'home_screen.dart';
import 'book_appointment.dart';
import 'screens/history.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _bottomNavIndex = 0;

  final List<Widget> _pages = [
    HomeScreen(),
    AppointmentPage(),
    HistoryPage(),
  ];

  final List<IconData> _iconList = [
    Icons.home_filled,
    Icons.add_rounded,
    Icons.history,
  ];

  final List<String> _titleList = [
    'Home',
    'Appointments',
    'History',
  ];

  PreferredSizeWidget? _buildAppBar() {
    if (_bottomNavIndex == 0) {
      return PreferredSize(
        preferredSize: Size.fromHeight(60),
        child: AppBar(
          backgroundColor: Colors.transparent,
          flexibleSpace: Container(
            decoration: BoxDecoration(
              image: DecorationImage(
                image: AssetImage('assets/appbar.png'),
                fit: BoxFit.cover,
              ),
            ),
          ),
          title: GestureDetector(
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => ProfilePage()),
              );
            },
            child: Row(
              children: [
                Padding(
                  padding: EdgeInsets.only(top: 10),
                  child: CircleAvatar(
                    backgroundImage: AssetImage('assets/pogisivenz.png'),
                    radius: 20,
                  ),
                ),
                SizedBox(width: 10),
                Text(
                  'Hi, Harold Pogi',
                  style: TextStyle(color: Color(0xFFE5D9F2)),
                ),
              ],
            ),
          ),
        ),
      );
    }
    return null;
  }

  Widget _buildBottomNavigationBar() {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white, // Background color for the dock
        borderRadius: BorderRadius.vertical(
          top: Radius.circular(30), // Rounded corners at the top
        ),
        boxShadow: [
          BoxShadow(color: Colors.black38, spreadRadius: 0, blurRadius: 10),
        ],
      ),
      margin: const EdgeInsets.only(bottom: 20.0), // Margin for the floating effect
      child: ClipRRect(
        borderRadius: BorderRadius.vertical(
          top: Radius.circular(30), // Match the top rounded corners
        ),
        child: BottomNavigationBar(
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(_iconList[0]),
              label: _titleList[0],
            ),
            BottomNavigationBarItem(
              icon: Icon(_iconList[1]),
              label: _titleList[1],
            ),
            BottomNavigationBarItem(
              icon: Icon(_iconList[2]),
              label: _titleList[2],
            ),
          ],
          currentIndex: _bottomNavIndex,
          selectedItemColor: const Color.fromARGB(255, 116, 23, 133),
          unselectedItemColor: Colors.black,
          onTap: (index) {
            setState(() {
              _bottomNavIndex = index;
            });
          },
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.transparent, // Make Scaffold background transparent
      appBar: _buildAppBar(),
      body: Stack(
        children: [
          Positioned.fill(
            child: Image.asset(
              'assets/splash.png',
              fit: BoxFit.cover,
            ),
          ),
          Positioned.fill(
            child: AnimatedSwitcher(
              duration: Duration(milliseconds: 300),
              child: _pages[_bottomNavIndex],
            ),
          ),
        ],
      ),
      bottomNavigationBar: _buildBottomNavigationBar(),
    );
  }
}

多次修改后仍无法实现目标效果,该如何解决?

解决方案

直接修改以下核心部分即可实现iPhone Dock样式:

1. 替换为模糊半透明背景

iPhone Dock的核心是半透明模糊效果,用BackdropFilter替换原来的纯色背景,同时调整边距让导航栏更窄:

Widget _buildBottomNavigationBar() {
  return Container(
    // 左右加边距,让导航栏宽度小于屏幕宽度,贴近iOS风格
    margin: const EdgeInsets.symmetric(horizontal: 20, bottom: 20),
    child: ClipRRect(
      borderRadius: BorderRadius.vertical(top: Radius.circular(30)),
      child: BackdropFilter(
        // 模糊程度可根据需求调整sigma值
        filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12),
        child: Container(
          decoration: BoxDecoration(
            // 半透明白色,配合模糊效果呈现毛玻璃质感
            color: Colors.white.withOpacity(0.75),
            borderRadius: BorderRadius.vertical(top: Radius.circular(30)),
            // 调整阴影方向,增强悬浮感
            boxShadow: [
              BoxShadow(
                color: Colors.black12,
                blurRadius: 8,
                offset: Offset(0, -2),
              ),
            ],
          ),
          child: BottomNavigationBar(
            // 固定模式,避免图标因数量变化移位
            type: BottomNavigationBarType.fixed,
            // 取消导航栏自带背景色,让模糊效果透出来
            backgroundColor: Colors.transparent,
            // 取消自带阴影,用自定义的阴影效果
            elevation: 0,
            items: <BottomNavigationBarItem>[
              BottomNavigationBarItem(
                icon: Icon(_iconList[0]),
                label: _titleList[0],
              ),
              // 可选:给中间加号图标添加背景,突出显示
              BottomNavigationBarItem(
                icon: Container(
                  padding: EdgeInsets.all(8),
                  decoration: BoxDecoration(
                    color: const Color.fromARGB(255, 116, 23, 133),
                    borderRadius: BorderRadius.circular(50),
                  ),
                  child: Icon(_iconList[1], color: Colors.white, size: 28),
                ),
                label: _titleList[1],
              ),
              BottomNavigationBarItem(
                icon: Icon(_iconList[2]),
                label: _titleList[2],
              ),
            ],
            currentIndex: _bottomNavIndex,
            selectedItemColor: const Color.fromARGB(255, 116, 23, 133),
            // 未选中图标用浅黑色,更贴近iOS视觉风格
            unselectedItemColor: Colors.black54,
            onTap: (index) {
              setState(() {
                _bottomNavIndex = index;
              });
            },
          ),
        ),
      ),
    ),
  );
}

2. 解决页面内容被遮挡问题

设置extendBody: true让页面内容延伸到导航栏下方,配合模糊背景更自然:

@override
Widget build(BuildContext context) {
  return Scaffold(
    backgroundColor: Colors.transparent,
    // 关键:让body延伸到底部导航栏下方
    extendBody: true,
    appBar: _buildAppBar(),
    body: Stack(
      children: [
        Positioned.fill(
          child: Image.asset(
            'assets/splash.png',
            fit: BoxFit.cover,
          ),
        ),
        Positioned.fill(
          child: AnimatedSwitcher(
            duration: Duration(milliseconds: 300),
            child: _pages[_bottomNavIndex],
          ),
        ),
      ],
    ),
    bottomNavigationBar: _buildBottomNavigationBar(),
  );
}

3. 细节优化(可选)

  • 如果页面内容底部需要避开导航栏,可以给_pages中的每个页面添加Padding(bottom: kBottomNavigationBarHeight + 20)
  • 调整模糊的sigmaX/Y值可以改变毛玻璃的模糊程度,数值越大越模糊

完成以上修改后,底部导航栏就会具备iPhone Dock的悬浮模糊、顶部圆角、窄边距等特征,和目标效果一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 13:19:50