You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:点击CupertinoButton时FullscreenDialog无退场动画问题求助

问题分析

当前代码存在两个核心问题:

  1. 返回箭头的退场动画未完整执行就直接调用pop(),逻辑上存在瑕疵(虽视觉上看似正常);
  2. 子组件(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 10:27:55