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: '我的'), ], ), ), ); } }
关键部分解释
- 滚动监听:
NotificationListener能捕获滚动组件的所有滚动通知,我们通过对比当前和上一次的滚动位置,判断用户的滚动方向。 - 阈值控制:设置
_scrollThreshold是为了避免用户轻微滚动(比如手抖)就触发动画,让交互更自然。 - 动画实现:
AnimatedContainer会自动处理状态变化时的过渡动画,这里用Matrix4.translationValues控制导航栏的垂直位移,隐藏时把它移出屏幕底部。 - 屏幕适配:用
MediaQuery.of(context).size.height * 0.1计算导航栏的高度,确保在不同屏幕上都能完全隐藏。
可选优化
- 如果你的列表是
CustomScrollView,监听逻辑完全一致,只需把NotificationListener套在CustomScrollView外面即可。 - 可以调整动画的
duration和curve参数,让动画速度和曲线更贴合你的App风格。 - 如果需要在滚动到顶部时强制显示导航栏,可以在判断逻辑里加入
notification.metrics.atEdge的判断。
内容的提问来源于stack exchange,提问作者Mohith7548
相关产品推荐
相关产品推荐

