Flutter如何实现点击按钮控制父Container高度展开收起
Flutter 可折叠展开容器实现方案
实现逻辑
核心通过状态变量控制容器高度切换,配合动画组件让高度变化更自然:
- 用
Stack布局实现右上角按钮悬浮在容器右上角的效果 - 定义布尔状态值标记当前是展开/收起状态
- 点击按钮时切换状态值,通过
setState触发组件重建,动态修改容器高度参数 - 用
AnimatedContainer自带的动画过渡,避免高度切换生硬
完整可运行代码
import 'package:flutter/material.dart'; class ExpandableContainer extends StatefulWidget { // 支持外部自定义收起高度、背景色、内容组件、圆角 final double collapsedHeight; final Widget child; final Color? backgroundColor; final double borderRadius; const ExpandableContainer({ super.key, this.collapsedHeight = 100, required this.child, this.backgroundColor, this.borderRadius = 8, }); @override State<ExpandableContainer> createState() => _ExpandableContainerState(); } class _ExpandableContainerState extends State<ExpandableContainer> { // 初始默认收起状态 bool _isExpanded = false; @override Widget build(BuildContext context) { return Stack( children: [ // 动态切换高度的内容容器 AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, height: _isExpanded ? null : widget.collapsedHeight, width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: widget.backgroundColor ?? Colors.white, borderRadius: BorderRadius.circular(widget.borderRadius), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.2), blurRadius: 4, offset: const Offset(0, 2), ) ], ), // 收起时裁剪超出内容,禁止内部滚动 child: SingleChildScrollView( physics: const NeverScrollableScrollPhysics(), child: widget.child, ), ), // 右上角展开/收起控制按钮 Positioned( top: 0, right: 0, child: IconButton( onPressed: () { setState(() { _isExpanded = !_isExpanded; }); }, icon: Icon( _isExpanded ? Icons.expand_less : Icons.expand_more, size: 28, ), ), ) ], ); } } // 组件调用示例 class DemoPage extends StatelessWidget { const DemoPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('可展开容器示例')), body: Padding( padding: const EdgeInsets.all(16), child: ExpandableContainer( collapsedHeight: 120, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: const [ Text('标题内容', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), SizedBox(height: 12), Text('这是一段测试长文本,用来演示容器收起和展开的效果,收起状态下只会展示固定高度内的内容,超出部分会被裁剪,点击右上角的展开按钮后,容器会自动过渡到适配内容的完整高度,再次点击按钮就会收回到初始的最小高度。你可以把任意自定义组件作为child传入,包括列表、图片、表单组件都能正常适配。'), SizedBox(height: 20), Text('其他补充内容', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)), SizedBox(height: 8), Text('1. 支持自定义收起时的固定高度\n2. 支持自定义容器背景色、圆角\n3. 自带300毫秒的平滑过渡动画\n4. 图标会跟随状态自动切换为向上/向下箭头'), ], ), ), ), ); } }
关键注意点
- 收起状态下容器设置固定高度,超出内容会被自动裁剪,不需要额外处理内容溢出
- 展开状态下将
height设为null,容器会自动适配内部child的完整高度 - 动画时长、曲线可以根据业务需求调整,不需要动画的话直接替换成普通
Container即可 - 如果内部内容本身是可滚动的(比如长列表),可以去掉外层的
SingleChildScrollView,避免滑动冲突
内容的提问来源于stack exchange,提问作者P Abhiram
相关产品推荐
相关产品推荐

