Flutter问题:如何将组件高度动画恢复至原始内容高度
解决AnimatedContainer动画恢复到内容默认高度的问题
核心问题在于:AnimatedContainer的隐式动画需要明确的数值起点和终点,直接设置height: null会跳过动画逻辑(直接让子组件决定高度),同时还会触发非空类型错误。要实现动画过渡到内容高度,得先获取内容的实际高度,再基于这个数值做动画。
方案一:GlobalKey获取内容高度 + 隐式动画
先通过GlobalKey拿到子组件的实际高度,再让AnimatedContainer动画到这个值:
GlobalKey _contentKey = GlobalKey(); double? height = 100; @override Widget build(BuildContext context) { return Scaffold( body: GestureDetector( onTap: () { setState(() { if (height == 100) { // 获取子组件的实际高度 final renderBox = _contentKey.currentContext?.findRenderObject() as RenderBox; height = renderBox.size.height; } else { height = 100; } }); }, child: Center( child: AnimatedContainer( curve: Curves.bounceOut, duration: const Duration(milliseconds: 500), width: 100, height: height, color: Colors.deepPurple, child: Text( 'This text set to be much larger than 100 aaaaaaaaaa aaaaaaaa aaaaaaaaa aaaaaaaa.', style: TextStyle(fontSize: 30, color: Colors.white), key: _contentKey, ), ), ), ), ); }
说明:
- 用
GlobalKey绑定子组件,点击时通过RenderBox获取其实际高度 - 切换时要么回到固定高度100,要么动画到内容高度,全程都是数值过渡,符合隐式动画的要求
方案二:显式动画(AnimationController + AnimatedBuilder)
如果需要更精细的控制,比如自定义动画曲线、监听动画状态,可以用显式动画:
final _controller = AnimationController( duration: const Duration(milliseconds: 500), vsync: this, ); late Animation<double> _heightAnimation; GlobalKey _contentKey = GlobalKey(); bool _isCollapsed = true; double _contentHeight = 0; @override void initState() { super.initState(); // 先获取内容高度(build完成后执行) WidgetsBinding.instance.addPostFrameCallback((_) { final renderBox = _contentKey.currentContext?.findRenderObject() as RenderBox; _contentHeight = renderBox.size.height; _heightAnimation = Tween<double>(begin: 100, end: _contentHeight).animate( CurvedAnimation(parent: _controller, curve: Curves.bounceOut), ); }); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: GestureDetector( onTap: () { if (_isCollapsed) { _controller.forward(); } else { _controller.reverse(); } _isCollapsed = !_isCollapsed; }, child: Center( child: AnimatedBuilder( animation: _heightAnimation, builder: (context, child) { return Container( width: 100, height: _heightAnimation.value, color: Colors.deepPurple, child: child, ); }, child: Text( 'This text set to be much larger than 100 aaaaaaaaaa aaaaaaaa aaaaaaaaa aaaaaaaa.', style: TextStyle(fontSize: 30, color: Colors.white), key: _contentKey, ), ), ), ), ); }
说明:
- 用
AnimationController控制动画的播放/反向 - 初始化时通过
addPostFrameCallback获取内容的实际高度,创建对应的Tween动画 - 点击时触发控制器的forward/reverse,实现展开/折叠的动画过渡
为什么直接用null不行?
当AnimatedContainer的height设为null时,它会直接采用子组件的尺寸布局,不会生成中间的动画过渡值——隐式动画的核心是基于数值变化生成过渡帧,null不属于可插值的数值类型,所以无法触发动画。必须先拿到内容的具体高度数值,才能让动画系统生成过渡效果。
内容的提问来源于stack exchange,提问作者enchance
相关产品推荐
相关产品推荐

