Flutter:点击CupertinoButton时FullscreenDialog无退场动画问题求助
问题分析
当前代码存在两个核心问题:
- 返回箭头的退场动画未完整执行就直接调用
pop(),逻辑上存在瑕疵(虽视觉上看似正常); - 子组件(
NameForm的按钮)直接调用Navigator.pop(),跳过了FullscreenDialog的退场动画逻辑,导致对话框直接消失。
修复方案
1. 重构FullscreenDialog,通过回调传递关闭逻辑
修改FullscreenDialog的构造,让body接收一个关闭回调函数,同时封装统一的关闭方法,确保动画执行完成后再关闭页面:
import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; class FullscreenDialog extends StatefulWidget { final Widget title; // 修改body为带关闭回调的函数 final Widget Function(VoidCallback close) body; const FullscreenDialog({ super.key, required this.title, required this.body, }); @override State<FullscreenDialog> createState() => _FullscreenDialogState(); } class _FullscreenDialogState extends State<FullscreenDialog> with TickerProviderStateMixin { late AnimationController _primary, _secondary; late Animation<double> _animationPrimary, _animationSecondary; @override void initState() { _primary = AnimationController( vsync: this, duration: const Duration(seconds: 1), ); _animationPrimary = Tween<double>(begin: 0, end: 1) .animate(CurvedAnimation(parent: _primary, curve: Curves.easeOut)); // 绑定secondary动画与primary动画,保持背景过渡同步 _secondary = AnimationController( vsync: this, duration: const Duration(seconds: 1), ); _animationSecondary = Tween<double>(begin: 0, end: 1) .animate(CurvedAnimation(parent: _secondary, curve: Curves.easeOut)); _primary.addListener(() => _secondary.value = _primary.value); _primary.forward(); super.initState(); } @override void dispose() { _primary.dispose(); _secondary.dispose(); super.dispose(); } // 统一关闭逻辑:执行退场动画后再pop页面 void _closeDialog() { _primary.reverse().then((_) => Navigator.of(context).pop()); } @override Widget build(BuildContext context) { return CupertinoFullscreenDialogTransition( primaryRouteAnimation: _animationPrimary, secondaryRouteAnimation: _animationSecondary, linearTransition: true, child: Dismissible( direction: DismissDirection.down, key: UniqueKey(), onDismissed: (_) => Navigator.of(context).pop(), child: Scaffold( appBar: AppBar( title: widget.title, leading: Transform.rotate( angle: -90 * math.pi / 180, child: IconButton( icon: const Icon(Icons.arrow_back_ios), onPressed: _closeDialog, // 使用统一关闭方法 ), ), ), // 将关闭回调传递给body组件 body: widget.body(_closeDialog), ), ), ); } }
2. 修改NameForm,使用传递的关闭回调
在NameForm中接收FullscreenDialog传入的关闭函数,点击按钮时调用该函数触发带动画的关闭:
import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; class NameForm extends StatelessWidget { const NameForm({super.key}); @override Widget build(BuildContext context) { final formKey = GlobalKey<FormState>(); String name = ''; return FullscreenDialog( title: const Text('Add a Name'), // 接收关闭回调 body: (close) { return Form( key: formKey, child: ListView( physics: const ClampingScrollPhysics(), children: [ TextFormField( autofocus: true, autovalidateMode: AutovalidateMode.onUserInteraction, initialValue: name, onChanged: (value) => name = value.trim(), decoration: const InputDecoration( labelText: 'Name', hintText: 'Name', ), validator: (value) => value!.isEmpty ? 'Please enter a name' : null, keyboardType: TextInputType.text, textCapitalization: TextCapitalization.words, ), const SizedBox(height: 30), CupertinoButton.filled( onPressed: () { if (formKey.currentState!.validate()) { close(); // 调用关闭回调触发动画 } }, child: const Text('Add name'), ), ], ), ); }, ); } }
优化说明
- 统一了关闭逻辑:返回箭头、按钮点击都会先执行完整的退场动画,再关闭页面;
- 绑定了
primary与secondary动画,让背景过渡效果与对话框的升起/退场保持同步,提升视觉一致性; - 通过回调传递关闭函数,实现了子组件与父组件的解耦,保持了
FullscreenDialog的通用性。
内容的提问来源于stack exchange,提问作者cherookie
相关产品推荐
相关产品推荐

