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

Flutter实现滚动时隐藏Bottom Navigation Bar(带滑动动画)

Flutter实现滚动时底部导航栏的隐藏/显示动画

嘿,这个需求我之前帮不少开发者实现过,核心就是监听滚动方向,再配合动画组件控制底部导航栏的显隐,下面给你一个完整的可运行示例,我会一步步拆解关键部分:

核心实现思路

  • 用NotificationListener<ScrollNotification>监听列表的滚动事件,判断用户是向上还是向下滚动
  • 通过AnimatedContainer给底部导航栏添加平滑的滑动动画(也可以用SlideTransition,原理一致)
  • 加入滚动阈值,避免轻微滚动就触发动画,提升交互体验

完整代码示例

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(
      title: 'Scroll Hide Bottom Nav',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const BlogListPage(),
    );
  }
}

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

  @override
  State<BlogListPage> createState() => _BlogListPageState();
}

class _BlogListPageState extends State<BlogListPage> {
  // 控制底部导航栏的可见状态
  bool _isNavVisible = true;
  // 记录上一次滚动位置,用来判断滚动方向
  double _lastScrollPosition = 0;
  // 滚动阈值:超过这个距离才触发动画,避免误触
  final double _scrollThreshold = 10;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('博客列表')),
      // 用NotificationListener包裹列表,监听滚动事件
      body: NotificationListener<ScrollNotification>(
        onNotification: (notification) {
          final currentScrollPosition = notification.metrics.pixels;
          final scrollDelta = currentScrollPosition - _lastScrollPosition;

          // 超过阈值才更新状态,避免轻微滚动触发动画
          if (scrollDelta.abs() > _scrollThreshold) {
            if (scrollDelta > 0 && _isNavVisible) {
              // 向下滚动 → 隐藏导航栏
              setState(() => _isNavVisible = false);
            } else if (scrollDelta < 0 && !_isNavVisible) {
              // 向上滚动 → 显示导航栏
              setState(() => _isNavVisible = true);
            }
            _lastScrollPosition = currentScrollPosition;
          }
          return true;
        },
        // 模拟博客文章列表
        child: ListView.builder(
          itemCount: 30,
          itemBuilder: (context, index) {
            return Padding(
              padding: const EdgeInsets.all(16.0),
              child: Card(
                child: Padding(
                  padding: const EdgeInsets.all(20.0),
                  child: Text(
                    '博客文章 #${index + 1}',
                    style: const TextStyle(fontSize: 18),
                  ),
                ),
              ),
            );
          },
        ),
      ),
      // 用AnimatedContainer实现导航栏的滑动动画
      bottomNavigationBar: AnimatedContainer(
        duration: const Duration(milliseconds: 200),
        curve: Curves.easeInOut,
        // 根据可见状态调整位置:隐藏时向下移出屏幕
        transform: Matrix4.translationValues(
          0,
          _isNavVisible ? 0 : MediaQuery.of(context).size.height * 0.1,
          0,
        ),
        child: BottomNavigationBar(
          items: const [
            BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
            BottomNavigationBarItem(icon: Icon(Icons.search), label: '搜索'),
            BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'),
          ],
        ),
      ),
    );
  }
}

关键部分解释

  1. 滚动监听:NotificationListener能捕获滚动组件的所有滚动通知,我们通过对比当前和上一次的滚动位置,判断用户的滚动方向。
  2. 阈值控制:设置_scrollThreshold是为了避免用户轻微滚动(比如手抖)就触发动画,让交互更自然。
  3. 动画实现:AnimatedContainer会自动处理状态变化时的过渡动画,这里用Matrix4.translationValues控制导航栏的垂直位移,隐藏时把它移出屏幕底部。
  4. 屏幕适配:用MediaQuery.of(context).size.height * 0.1计算导航栏的高度,确保在不同屏幕上都能完全隐藏。

可选优化

  • 如果你的列表是CustomScrollView,监听逻辑完全一致,只需把NotificationListener套在CustomScrollView外面即可。
  • 可以调整动画的duration和curve参数,让动画速度和曲线更贴合你的App风格。
  • 如果需要在滚动到顶部时强制显示导航栏,可以在判断逻辑里加入notification.metrics.atEdge的判断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 10:02:25