Flutter:如何为Flex内组件的展开与普通状态添加过渡动画
Flex组件内Expanded状态切换的平滑动画实现
需求
在Flex(如Column/Row)组件内,实现子组件从Expanded状态切换为普通状态时的尺寸动画过渡,同时让Flex的其他子组件也平滑过渡到新位置。
当前问题
现有代码切换状态时尺寸直接跳转,无动画效果:
class MyHomePage extends Stateful Widget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { bool expand = false; void _expandToggle() { setState(() => expand = !expand); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'Whether the child below is expanded:', ), Expanded( flex: expand ? 1 : 0, child: AnimatedSize( duration: const Duration(seconds: 3), child: ColoredBox( color: Colors.yellow, child: Text( '$expand', style: Theme.of(context).textTheme.headlineMedium, ), ), ), ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _expandToggle, child: const Icon(Icons.add), ), ); } }
问题原因
Expanded是Flex布局的约束组件,必须直接作为Flex的子组件,无法被AnimatedSize包裹——这也是直接嵌套无效的核心原因。
解决方案
方案一:AnimatedContainer + Flexible
通过Flexible让容器获取剩余空间权限,结合AnimatedContainer动态修改高度(Row场景改宽度)实现动画:
class MyHomePage extends Stateful Widget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { bool expand = false; void _expandToggle() { setState(() => expand = !expand); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'Whether the child below is expanded:', ), Flexible( child: AnimatedContainer( duration: const Duration(seconds: 3), curve: Curves.easeInOut, // 展开时占满剩余空间,收起时高度归0 height: expand ? double.infinity : 0, child: ColoredBox( color: Colors.yellow, child: Text( '$expand', style: Theme.of(context).textTheme.headlineMedium, ), ), ), ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _expandToggle, child: const Icon(Icons.add), ), ); } }
方案二:AnimatedCrossFade
用AnimatedCrossFade在收起状态的空容器和展开状态的Expanded组件之间做过渡动画:
class MyHomePage extends Stateful Widget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { bool expand = false; void _expandToggle() { setState(() => expand = !expand); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'Whether the child below is expanded:', ), AnimatedCrossFade( duration: const Duration(seconds: 3), sizeCurve: Curves.easeInOut, // 收起状态:高度为0的空容器 firstChild: const SizedBox(height: 0), // 展开状态:带Expanded的目标组件 secondChild: Expanded( child: ColoredBox( color: Colors.yellow, child: Text( '$expand', style: Theme.of(context).textTheme.headlineMedium, ), ), ), // 根据状态切换显示的组件 crossFadeState: expand ? CrossFadeState.showSecond : CrossFadeState.showFirst, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _expandToggle, child: const Icon(Icons.add), ), ); } }
方案说明
- 方案一:适合需要精确控制尺寸变化过程的场景,
Flexible保证容器能利用剩余空间,AnimatedContainer自动处理尺寸动画,其他子组件会随布局变化平滑移动。 - 方案二:适合需要同时加入淡入淡出效果的场景,通过组件切换实现过渡,代码更简洁。
内容的提问来源于stack exchange,提问作者JakesMD
相关产品推荐
相关产品推荐

