Flutter实现滚动时可滚动Widget互相填充空间的动画效果
Flutter双列表联动滚动实现
需求说明
- 顶部紫色
SizedBox保持固定位置,不随滚动变化 - 滚动底部列表(BottomList)时,顶部列表(TopList)收缩至最小高度,底部列表上移占据顶部列表的空间;当顶部列表到达最小高度后,底部列表正常滚动
- 滚动顶部列表时,顶部列表恢复至初始设定高度,底部列表回到原位置
实现代码
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( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { late final ScrollController _topListController; late final ScrollController _bottomListController; late double _topListMaxHeight; late double _topListMinHeight; late double _currentTopListHeight; @override void initState() { super.initState(); _topListController = ScrollController()..addListener(_onTopListScroll); _bottomListController = ScrollController()..addListener(_onBottomListScroll); // 计算顶部列表的最大高度和最小高度 final screenHeight = MediaQuery.of(context).size.height; _topListMaxHeight = screenHeight / 2 - screenHeight / 5 - kToolbarHeight; _topListMinHeight = 80; // 自定义最小高度 _currentTopListHeight = _topListMaxHeight; } void _onBottomListScroll() { // 底部列表向下滚动时,收缩顶部列表 if (_bottomListController.position.userScrollDirection == ScrollDirection.reverse) { if (_currentTopListHeight > _topListMinHeight) { setState(() { _currentTopListHeight = _topListMinHeight; }); // 重置顶部列表滚动位置,避免滚动冲突 _topListController.jumpTo(0); } } } void _onTopListScroll() { // 顶部列表向上滚动时,恢复最大高度 if (_topListController.position.userScrollDirection == ScrollDirection.forward) { if (_currentTopListHeight < _topListMaxHeight) { setState(() { _currentTopListHeight = _topListMaxHeight; }); // 重置底部列表滚动位置 _bottomListController.jumpTo(0); } } } @override void dispose() { _topListController.dispose(); _bottomListController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('two scrolls')), body: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 固定位置的紫色SizedBox SizedBox( height: MediaQuery.of(context).size.height / 5, child: Container( color: Colors.purple, ), ), // 可收缩的顶部列表 AnimatedContainer( duration: const Duration(milliseconds: 300), height: _currentTopListHeight, child: ListView( controller: _topListController, children: [ Container(width: 160, height: 160, color: Colors.red), Container(width: 160, height: 160, color: Colors.blue), Container(width: 160, height: 160, color: Colors.red), Container(width: 160, height: 160, color: Colors.blue), Container(width: 160, height: 160, color: Colors.red), ], ), ), const Divider(height: 16, thickness: 0.5, color: Colors.black), // 可上移的底部列表 Expanded( child: ListView( controller: _bottomListController, children: [ Container(width: 160, height: 160, color: Colors.red), Container(width: 160, height: 160, color: Colors.blue), Container(width: 160, height: 160, color: Colors.red), Container(width: 160, height: 160, color: Colors.blue), Container(width: 160, height: 160, color: Colors.red), ], ), ) ], ), ); } }
实现要点
- 使用
ScrollController监听两个列表的滚动方向,判断触发收缩/恢复操作的时机 - 用
AnimatedContainer实现顶部列表高度变化的平滑过渡 - 在触发高度变化时,重置对应列表的滚动位置,避免滚动状态冲突
- 可通过修改
_topListMinHeight自定义顶部列表的最小收缩高度
内容的提问来源于stack exchange,提问作者vladli
相关产品推荐
相关产品推荐

