Flutter如何将ScrollController传给其他组件实现滚动隐藏底部导航栏
解决方案
方案1:NotificationListener监听滚动通知(推荐,无代码侵入)
这个方案无需传递ScrollController,也不需要修改子页面代码,利用Flutter滚动事件冒泡机制实现:
- 首先在持有BottomNavigationBar的父组件中定义显隐状态与过渡动画:
bool _isBottomNavVisible = true; late AnimationController _animationController; late Animation<Offset> _offsetAnimation; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 200), ); _offsetAnimation = Tween<Offset>( begin: Offset.zero, end: const Offset(0, 1), ).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, )); } @override void dispose() { _animationController.dispose(); super.dispose(); }
- 给父组件Scaffold的body外层包裹
NotificationListener监听滚动事件,修改底部导航的显隐状态:
@override Widget build(BuildContext context) { return Scaffold( body: NotificationListener<UserScrollNotification>( onNotification: (notification) { // 仅处理垂直滚动事件 if (notification.metrics.axis != Axis.vertical) return false; if (notification.direction == ScrollDirection.forward) { // 向上滚动(内容向下移动)显示底部导航 if (!_isBottomNavVisible) { _isBottomNavVisible = true; _animationController.reverse(); } } else if (notification.direction == ScrollDirection.reverse) { // 向下滚动(内容向上移动)隐藏底部导航 if (_isBottomNavVisible) { _isBottomNavVisible = false; _animationController.forward(); } } // 返回false不拦截事件,不影响子页面原有滚动逻辑 return false; }, child: pageList[_currentIndex], ), // 底部导航用SlideTransition包裹实现平滑过渡 bottomNavigationBar: SlideTransition( position: _offsetAnimation, child: BottomNavigationBar( currentIndex: _currentIndex, onTap: (index) => setState(() => _currentIndex = index), items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: "页面1"), BottomNavigationBarItem(icon: Icon(Icons.category), label: "页面2"), BottomNavigationBarItem(icon: Icon(Icons.message), label: "页面3"), BottomNavigationBarItem(icon: Icon(Icons.person), label: "页面4"), ], ), ), ); }
优势:适配所有子页面的内置可滚动组件(ListView/GridView/ SingleChildScrollView等),不需要对子页面做任何修改,也不存在控制器跨页面复用的冲突问题
方案2:共享ScrollController(适合需要精细控制滚动的场景)
如果你需要基于滚动位置做更多自定义逻辑,可以通过状态管理共享滚动控制器:
- 第一步:定义共享的控制器管理类
import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; class ScrollNavController extends ChangeNotifier { final ScrollController scrollController = ScrollController(); bool _isNavVisible = true; bool get isNavVisible => _isNavVisible; ScrollNavController() { scrollController.addListener(_handleScroll); } void _handleScroll() { final direction = scrollController.position.userScrollDirection; if (direction == ScrollDirection.reverse && _isNavVisible) { _isNavVisible = false; notifyListeners(); } else if (direction == ScrollDirection.forward && !_isNavVisible) { _isNavVisible = true; notifyListeners(); } } @override void dispose() { scrollController.dispose(); super.dispose(); } }
- 第二步:在父组件上层用你常用的状态管理工具(Provider/Bloc/GetX等)注入
ScrollNavController实例,父组件监听isNavVisible变化控制底部导航显隐 - 第三步:每个子页面的
ListView.builder绑定共享的滚动控制器即可:
ListView.builder( controller: Provider.of<ScrollNavController>(context).scrollController, // 原有列表配置 itemCount: 100, itemBuilder: (context, index) => ListTile(title: Text("项$index")), )
注意:切换页面时如果需要保留每个页面的滚动位置,需要给每个子页面单独分配控制器,监听所有控制器的滚动事件即可,不需要共用同一个控制器。
内容的提问来源于stack exchange,提问作者Oasys
相关产品推荐
相关产品推荐

