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

如何在NestedScrollView中实现双SliverAppBar的特定滚动行为

实现NestedScrollView内SliverAppBar的底部触发显示效果

你需要实现的是NestedScrollView中第一个SliverAppBar向下滚动隐藏、仅当向上滚动到列表底部时才平滑显示的效果,以下是基于你现有代码的改造方案:

核心思路

  • 将组件改为StatefulWidget,用于维护滚动状态和AppBar的显示状态
  • 通过ScrollController监听列表滚动,判断是否到达底部
  • 结合动画控制器控制第一个SliverAppBar的显示/隐藏过渡,保证平滑效果

完整代码实现

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

  @override
  State<CustomSliverAppBarWidget> createState() => _CustomSliverAppBarWidgetState();
}

class _CustomSliverAppBarWidgetState extends State<CustomSliverAppBarWidget> with SingleTickerProviderStateMixin {
  late final ScrollController _scrollController;
  late final AnimationController _animationController;
  bool _showTopAppBar = false;

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController()..addListener(_onScroll);
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
      value: 0,
    );
  }

  void _onScroll() {
    // 预留20像素阈值,避免滚动到底部时的抖动问题
    final isAtBottom = _scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 20;
    if (isAtBottom != _showTopAppBar) {
      setState(() {
        _showTopAppBar = isAtBottom;
      });
      // 根据状态播放显示/隐藏动画
      _showTopAppBar ? _animationController.forward() : _animationController.reverse();
    }
  }

  @override
  void dispose() {
    _scrollController.dispose();
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        headerSliverBuilder: (context, innerBoxIsScrolled) {
          return [
            // 第一个SliverAppBar:仅在滚动到底部时平滑显示
            SliverVisibility(
              visible: _animationController.value > 0,
              sliver: SliverAppBar(
                pinned: false,
                floating: false,
                snap: false,
                toolbarHeight: Tween<double>(begin: 0, end: kToolbarHeight).animate(_animationController).value,
                title: Opacity(
                  opacity: _animationController.value,
                  child: const Text("Something"),
                ),
              ),
            ),
            // 第二个固定在顶部的SliverAppBar
            const SliverAppBar(
              pinned: true,
              title: Text('Sliver AppBar 2'),
              backgroundColor: Colors.green,
            ),
          ];
        },
        body: ListView.builder(
          controller: _scrollController,
          itemCount: 100,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text('Item #$index'),
            );
          },
        ),
      ),
    );
  }
}

关键细节说明

  • 滚动判断:通过ScrollController的maxScrollExtent判断是否到达列表底部,预留20像素阈值避免滚动边界的抖动
  • 动画控制:使用AnimationController实现300毫秒的平滑过渡,同时通过Tween动态修改AppBar的高度,保证显示/隐藏过程自然
  • 嵌套兼容性:保持NestedScrollView结构,完全适配后续添加TabBar的需求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:49:58