Flutter:表单提交验证通过后带动画移除屏幕中的卡片
表单卡片动画移除的最佳实现方式
你当前用Future.delayed(Duration.zero)获取尺寸的方法能实现效果,但有明显缺陷:比如布局变动(屏幕旋转、父组件尺寸变化)时,预先获取的宽高不会同步更新,可能导致动画错位;而且只是把尺寸缩到0,卡片实际还留在组件树里,不够优雅。下面是几种更可靠的实现方案:
方案一:AnimatedContainer + 尺寸获取 + 动画后移除组件
这种方式保留你获取尺寸的思路,但优化动画逻辑,确保动画结束后彻底移除组件:
class FormCard extends StatefulWidget { @override _FormCardState createState() => _FormCardState(); } class _FormCardState extends State<FormCard> { final _formKey = GlobalKey<FormState>(); final _cardKey = GlobalKey(); double? _cardHeight; double? _cardWidth; bool _isRemoving = false; bool _isRemoved = false; @override void initState() { super.initState(); // 组件渲染完成后获取初始尺寸 WidgetsBinding.instance.addPostFrameCallback((_) { final size = _cardKey.currentContext?.size; if (size != null) { setState(() { _cardHeight = size.height; _cardWidth = size.width; }); } }); } void _submitForm() { if (_formKey.currentState!.validate()) { setState(() => _isRemoving = true); // 动画结束后标记组件为已移除 Future.delayed(const Duration(milliseconds: 300), () { setState(() => _isRemoved = true); }); } } @override Widget build(BuildContext context) { if (_isRemoved) return const SizedBox.shrink(); return AnimatedContainer( key: _cardKey, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, width: _isRemoving ? 0 : _cardWidth, height: _isRemoving ? 0 : _cardHeight, opacity: _isRemoving ? 0 : 1, child: Card( child: Form( key: _formKey, child: Column( children: [ // 表单字段 TextFormField(validator: (val) => val?.isEmpty ?? true ? '必填' : null), ElevatedButton(onPressed: _submitForm, child: const Text('提交')), ], ), ), ), ); } }
这里用WidgetsBinding.instance.addPostFrameCallback替代Future.delayed(Duration.zero),更可靠地获取组件渲染后的尺寸;动画结束后通过_isRemoved状态把卡片替换为空组件,彻底从组件树移除。
方案二:SizeTransition + AnimatedOpacity 组合(无需手动获取尺寸)
这个方案不需要手动获取卡片尺寸,利用Flutter内置的过渡组件自动处理缩放和淡入淡出:
class FormCard extends StatefulWidget { @override _FormCardState createState() => _FormCardState(); } class _FormCardState extends State<FormCard> with SingleTickerProviderStateMixin { final _formKey = GlobalKey<FormState>(); late AnimationController _animationController; bool _isRemoved = false; @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); } void _submitForm() { if (_formKey.currentState!.validate()) { _animationController.forward().then((_) { setState(() => _isRemoved = true); }); } } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { if (_isRemoved) return const SizedBox.shrink(); return AnimatedOpacity( opacity: _animationController.isCompleted ? 0 : 1 - _animationController.value, duration: const Duration(milliseconds: 300), child: SizeTransition( sizeFactor: CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, ), axisAlignment: 0.5, child: Card( child: Form( key: _formKey, child: Column( children: [ TextFormField(validator: (val) => val?.isEmpty ?? true ? '必填' : null), ElevatedButton(onPressed: _submitForm, child: const Text('提交')), ], ), ), ), ), ); } }
SizeTransition会基于卡片的固有尺寸自动计算缩放比例,配合AnimatedOpacity实现淡入淡出,不需要手动管理宽高,代码更简洁,适配性更强。
方案三:AnimatedSwitcher 一键切换动画
如果想更简单,用AnimatedSwitcher可以快速实现组件切换时的动画效果,不需要手动管理动画控制器:
class FormCard extends StatefulWidget { @override _FormCardState createState() => _FormCardState(); } class _FormCardState extends State<FormCard> { final _formKey = GlobalKey<FormState>(); bool _showCard = true; void _submitForm() { if (_formKey.currentState!.validate()) { setState(() => _showCard = false); } } @override Widget build(BuildContext context) { return AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (child, animation) { return ScaleTransition( scale: animation, child: FadeTransition( opacity: animation, child: child, ), ); }, child: _showCard ? Card( key: const ValueKey('form-card'), child: Form( key: _formKey, child: Column( children: [ TextFormField(validator: (val) => val?.isEmpty ?? true ? '必填' : null), ElevatedButton(onPressed: _submitForm, child: const Text('提交')), ], ), ), ) : const SizedBox.shrink(key: ValueKey('empty')), ); } }
只需切换_showCard状态,AnimatedSwitcher就会自动执行你定义的缩放+淡入淡出动画,代码量最少,适合快速实现需求。
内容的提问来源于stack exchange,提问作者Mert Doğruca
相关产品推荐
相关产品推荐

