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

Flutter实现底部导航栏背景模糊效果的方法

实现Flutter底部导航栏背景模糊效果

我想在Flutter中实现底部导航栏模糊屏幕底部背景的效果(参考示例效果:示例效果),但目前做出的是带有上下厚边框的白色背景导航栏,会遮挡页面内容(当前效果:当前效果)。以下是我的代码:

class _MainPageState extends State<MainPage> {
  var currentIndex = 0;
  void onTabTapped(int index) {
    setState(() {
      currentIndex = index;
      HapticFeedback.lightImpact();
    });
  }

  final screens = [
    const HomePage(),
    NotificationPage(),
    MessagePage(),
    BookmarkPage(),
  ];

  List<IconData> listOfIcons = [
    Icons.home_rounded,
    Icons.notifications,
    Icons.message_rounded,
    Icons.library_books,
  ];

  List<String> listOfStrings = [
    'Home',
    'T.báo',
    'T.Nhắn',
    'B.Đăng',
  ];

  @override
  Widget build(BuildContext context) {
    double displayWidth = MediaQuery.of(context).size.width;
    return Scaffold(
      backgroundColor: Colors.white,
      body: SafeArea(child: screens[currentIndex]),
      bottomNavigationBar: Container(
        margin: EdgeInsets.all(displayWidth * .03),
        height: displayWidth * .155,
        decoration: BoxDecoration(
          color: Colors.white,
          boxShadow: [
            BoxShadow(
              color: Colors.black.withOpacity(.1),
              blurRadius: 30,
              offset: Offset(0, 10),
            ),
          ],
          borderRadius: BorderRadius.circular(50),
        ),
        child: ListView.builder(
          itemCount: 4,
          scrollDirection: Axis.horizontal,
          padding: EdgeInsets.symmetric(horizontal: displayWidth * .02),
          itemBuilder: (context, index) => InkWell(
            onTap: () {
              setState(() {
                currentIndex = index;
                HapticFeedback.lightImpact();
              });
            },
            splashColor: Colors.transparent,
            highlightColor: Colors.transparent,
            child: Stack(
              children: [
                AnimatedContainer(
                  duration: Duration(seconds: 1),
                  curve: Curves.fastLinearToSlowEaseIn,
                  width: index == currentIndex
                      ? displayWidth * .32
                      : displayWidth * .18,
                  alignment: Alignment.center,
                  child: AnimatedContainer(
                    duration: Duration(seconds: 1),
                    curve: Curves.fastLinearToSlowEaseIn,
                    height: index == currentIndex ? displayWidth * .12 : 0,
                    width: index == currentIndex ? displayWidth * .32 : 0,
                    decoration: BoxDecoration(
                      color: index == currentIndex
                          ? Color(0xffEC1C24).withOpacity(.2)
                          : Colors.transparent,
                      borderRadius: BorderRadius.circular(50),
                    ),
                  ),
                ),
                AnimatedContainer(
                  duration: Duration(seconds: 1),
                  curve: Curves.fastLinearToSlowEaseIn,
                  width: index == currentIndex
                      ? displayWidth * .31
                      : displayWidth * .18,
                  alignment: Alignment.center,
                  child: Stack(
                    children: [
                      Row(
                        children: [
                          AnimatedContainer(
                            duration: Duration(seconds: 1),
                            curve: Curves.fastLinearToSlowEaseIn,
                            width:
                                index == currentIndex ? displayWidth * .13 : 0,
                          ),
                          AnimatedOpacity(
                            opacity: index == currentIndex ? 1 : 0,
                            duration: Duration(seconds: 1),
                            curve: Curves.fastLinearToSlowEaseIn,
                            child: Text(
                              index == currentIndex
                                  ? '${listOfStrings[index]}'
                                  : '',
                              style: TextStyle(
                                color: Color(0xffEC1C24),
                                fontWeight: FontWeight.w600,
                                fontSize: 15,
                              ),
                            ),
                          ),
                        ],
                      ),
                      Row(
                        children: [
                          AnimatedContainer(
                            duration: Duration(seconds: 1),
                            curve: Curves.fastLinearToSlowEaseIn,
                            width:
                                index == currentIndex ? displayWidth * .03 : 20,
                          ),
                          Icon(
                            listOfIcons[index],
                            size: displayWidth * .076,
                            color: index == currentIndex
                                ? Color(0xffEC1C24)
                                : Colors.black26,
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

解决方案

要实现背景模糊效果,核心是使用BackdropFilter组件添加高斯模糊,同时调整导航栏背景的透明度,并让页面内容避开导航栏区域:

  1. 替换导航栏背景为模糊效果:用Stack包裹原导航栏内容,在底层添加BackdropFilter实现模糊,同时设置半透明背景色
  2. 避免内容被遮挡:给SafeArea包裹的页面内容添加底部padding,值等于导航栏的高度(含margin)
  3. 调整阴影与圆角:保留原有的阴影和圆角效果,确保视觉一致性

修改后的完整代码如下:

class _MainPageState extends State<MainPage> {
  var currentIndex = 0;
  void onTabTapped(int index) {
    setState(() {
      currentIndex = index;
      HapticFeedback.lightImpact();
    });
  }

  final screens = [
    const HomePage(),
    NotificationPage(),
    MessagePage(),
    BookmarkPage(),
  ];

  List<IconData> listOfIcons = [
    Icons.home_rounded,
    Icons.notifications,
    Icons.message_rounded,
    Icons.library_books,
  ];

  List<String> listOfStrings = [
    'Home',
    'T.báo',
    'T.Nhắn',
    'B.Đăng',
  ];

  @override
  Widget build(BuildContext context) {
    double displayWidth = MediaQuery.of(context).size.width;
    final navBarHeight = displayWidth * .155;
    final navBarMargin = displayWidth * .03;
    return Scaffold(
      backgroundColor: Colors.white,
      // 给页面内容添加底部padding,避免被导航栏遮挡
      body: SafeArea(
        child: Padding(
          padding: EdgeInsets.only(bottom: navBarHeight + navBarMargin * 2),
          child: screens[currentIndex],
        ),
      ),
      bottomNavigationBar: Container(
        margin: EdgeInsets.all(navBarMargin),
        height: navBarHeight,
        child: Stack(
          children: [
            // 背景模糊层
            ClipRRect(
              borderRadius: BorderRadius.circular(50),
              child: BackdropFilter(
                filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
                child: Container(
                  decoration: BoxDecoration(
                    color: Colors.white.withOpacity(0.7), // 半透明白色背景
                    borderRadius: BorderRadius.circular(50),
                    boxShadow: [
                      BoxShadow(
                        color: Colors.black.withOpacity(.1),
                        blurRadius: 30,
                        offset: const Offset(0, 10),
                      ),
                    ],
                  ),
                ),
              ),
            ),
            // 原导航栏内容
            ListView.builder(
              itemCount: 4,
              scrollDirection: Axis.horizontal,
              padding: EdgeInsets.symmetric(horizontal: displayWidth * .02),
              itemBuilder: (context, index) => InkWell(
                onTap: () {
                  setState(() {
                    currentIndex = index;
                    HapticFeedback.lightImpact();
                  });
                },
                splashColor: Colors.transparent,
                highlightColor: Colors.transparent,
                child: Stack(
                  children: [
                    AnimatedContainer(
                      duration: const Duration(seconds: 1),
                      curve: Curves.fastLinearToSlowEaseIn,
                      width: index == currentIndex
                          ? displayWidth * .32
                          : displayWidth * .18,
                      alignment: Alignment.center,
                      child: AnimatedContainer(
                        duration: const Duration(seconds: 1),
                        curve: Curves.fastLinearToSlowEaseIn,
                        height: index == currentIndex ? displayWidth * .12 : 0,
                        width: index == currentIndex ? displayWidth * .32 : 0,
                        decoration: BoxDecoration(
                          color: index == currentIndex
                              ? const Color(0xffEC1C24).withOpacity(.2)
                              : Colors.transparent,
                          borderRadius: BorderRadius.circular(50),
                        ),
                      ),
                    ),
                    AnimatedContainer(
                      duration: const Duration(seconds: 1),
                      curve: Curves.fastLinearToSlowEaseIn,
                      width: index == currentIndex
                          ? displayWidth * .31
                          : displayWidth * .18,
                      alignment: Alignment.center,
                      child: Stack(
                        children: [
                          Row(
                            children: [
                              AnimatedContainer(
                                duration: const Duration(seconds: 1),
                                curve: Curves.fastLinearToSlowEaseIn,
                                width: index == currentIndex
                                    ? displayWidth * .13
                                    : 0,
                              ),
                              AnimatedOpacity(
                                opacity: index == currentIndex ? 1 : 0,
                                duration: const Duration(seconds: 1),
                                curve: Curves.fastLinearToSlowEaseIn,
                                child: Text(
                                  index == currentIndex
                                      ? '${listOfStrings[index]}'
                                      : '',
                                  style: const TextStyle(
                                    color: Color(0xffEC1C24),
                                    fontWeight: FontWeight.w600,
                                    fontSize: 15,
                                  ),
                                ),
                              ),
                            ],
                          ),
                          Row(
                            children: [
                              AnimatedContainer(
                                duration: const Duration(seconds: 1),
                                curve: Curves.fastLinearToSlowEaseIn,
                                width: index == currentIndex
                                    ? displayWidth * .03
                                    : 20,
                              ),
                              Icon(
                                listOfIcons[index],
                                size: displayWidth * .076,
                                color: index == currentIndex
                                    ? const Color(0xffEC1C24)
                                    : Colors.black26,
                              ),
                            ],
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

关键修改说明

  • BackdropFilter:通过ImageFilter.blur设置模糊程度(sigmaX/Y值越大越模糊),配合ClipRRect保证模糊效果只在圆角范围内生效
  • 半透明背景:将原白色背景改为Colors.white.withOpacity(0.7),既保留白色基调又能透出底层页面内容
  • 底部padding:计算导航栏总高度(自身高度+上下margin),给页面内容添加对应padding,解决遮挡问题

内容的提问来源于stack exchange,提问作者Trong Nhan Nguyen Le

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 22:01:27