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

Flutter:实现滚动时容器折叠/展开效果(非SliverAppBar方案)

无需SliverAppBar实现滚动折叠/展开容器

针对你需要的「向下滚动折叠容器、向上滚动展开容器」的需求,这里提供一种不依赖SliverAppBar的实现方案,完全通过滚动监听+动画容器来实现,能避免Sliver组件可能带来的性能问题。

实现思路

通过监听滚动组件的滚动事件,判断滚动方向,动态调整目标容器的高度,并借助AnimatedContainer实现平滑的过渡动画,核心逻辑如下:

  • 用NotificationListener捕获滚动偏移量变化
  • 对比前后偏移量判断滚动方向(向上/向下)
  • 根据方向设置容器的目标高度(展开时固定高度/折叠时高度为0)
  • 利用动画容器自动完成平滑过渡

完整代码示例

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(
      home: const ScrollCollapsePage(),
    );
  }
}

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

  @override
  State<ScrollCollapsePage> createState() => _ScrollCollapsePageState();
}

class _ScrollCollapsePageState extends State<ScrollCollapsePage> {
  // 容器展开状态的高度
  final double _expandedHeight = 60;
  // 当前容器高度
  double _currentHeight = 60;
  // 记录上一次滚动的偏移量,用于判断方向
  double _lastScrollOffset = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          // 可折叠的搜索栏容器
          AnimatedContainer(
            duration: const Duration(milliseconds: 200),
            height: _currentHeight,
            color: Colors.blue[50],
            child: const Center(
              child: TextField(
                decoration: InputDecoration(
                  hintText: "搜索...",
                  border: InputBorder.none,
                  contentPadding: EdgeInsets.symmetric(horizontal: 16),
                ),
              ),
            ),
          ),
          // 滚动列表区域
          Expanded(
            child: NotificationListener<ScrollNotification>(
              onNotification: (notification) {
                final currentOffset = notification.metrics.pixels;
                
                // 向下滚动:折叠容器
                if (currentOffset > _lastScrollOffset && _currentHeight > 0) {
                  setState(() => _currentHeight = 0);
                }
                // 向上滚动:展开容器
                else if (currentOffset < _lastScrollOffset && _currentHeight < _expandedHeight) {
                  setState(() => _currentHeight = _expandedHeight);
                }
                
                _lastScrollOffset = currentOffset;
                return true;
              },
              child: ListView.builder(
                itemCount: 50,
                itemBuilder: (context, index) {
                  return ListTile(title: Text("列表项 $index"));
                },
              ),
            ),
          ),
        ],
      ),
    );
  }
}

优化建议

  1. 添加滚动阈值:可以设置最小滚动距离(比如10像素),只有当滚动超过该距离才触发折叠/展开,避免轻微滚动导致的频繁动画切换
  2. 渐进式动画:如果需要更细腻的效果,可替换AnimatedContainer为AnimationController,根据滚动距离逐步调整容器高度,而不是一次性切换
  3. 性能优化:确保列表使用ListView.builder(按需构建),避免一次性渲染所有列表项,减少卡顿概率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:45:58