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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 05:54:02