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")); }, ), ), ), ], ), ); } }
优化建议
- 添加滚动阈值:可以设置最小滚动距离(比如10像素),只有当滚动超过该距离才触发折叠/展开,避免轻微滚动导致的频繁动画切换
- 渐进式动画:如果需要更细腻的效果,可替换
AnimatedContainer为AnimationController,根据滚动距离逐步调整容器高度,而不是一次性切换 - 性能优化:确保列表使用
ListView.builder(按需构建),避免一次性渲染所有列表项,减少卡顿概率
内容的提问来源于stack exchange,提问作者Ali
相关产品推荐
相关产品推荐

