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

Flutter中GoRouter关闭AlertDialog时Pop动画丢失,如何无需延迟解决?

Flutter GoRouter 页面退出动画异常问题

我在Flutter中使用GoRouter处理导航,页面采用slide-up动画展示。但在关闭页面前弹出确认AlertDialog时,若不在调用context.pop()前添加Future.delayed(Duration(milliseconds: 200)),页面的slide-down过渡动画就不会播放——对话框和页面会直接关闭,跳过预期的动画效果。

我的AlertDialog代码

Future<bool?> _showBackDialog(BuildContext context) {
  return showDialog<bool>(
    context: context,
    builder: (BuildContext context) {
      return AlertDialog(
        title: const Text('确定离开?'),
        content: const Text('你确定要离开当前页面吗?'),
        actions: <Widget>[
          TextButton(
            style: TextButton.styleFrom(textStyle: Theme.of(context).textTheme.labelLarge),
            child: const Text('取消'),
            onPressed: () {
              context.pop(false);
            },
          ),
          TextButton(
            style: TextButton.styleFrom(textStyle: Theme.of(context).textTheme.labelLarge),
            child: const Text('退出'),
            onPressed: () {
              context.pop(true);
            },
          ),
        ],
      );
    },
  );
}

页面导航处理代码

...
return PopScope(
  canPop: false,
  onPopInvokedWithResult: (didPop, _) async {
    if (didPop) return;
    final bool shouldPop = await _showBackDialog(context) ?? false;
    if (shouldPop) {
      await Future.delayed(Duration(milliseconds: 200));
      if (context.mounted && context.canPop()) {
        context.pop();
      }
    }
  },
...

有没有无需使用人工延迟就能解决这个过渡动画问题的更好方法?


解决方案

问题根源是对话框关闭动画与页面退出动画同时触发,导致页面过渡动画被覆盖。可以通过以下两种方式避免冲突,无需依赖人工延迟:

方法1:使用根Navigator弹出对话框,分离上下文

修改对话框的弹出逻辑,使用rootNavigator: true让对话框挂载到根Navigator上,避免与页面上下文的动画冲突:

Future<bool?> _showBackDialog(BuildContext context) {
  return showDialog<bool>(
    context: context,
    rootNavigator: true, // 关键:使用根Navigator弹出对话框
    builder: (BuildContext context) {
      return AlertDialog(
        title: const Text('确定离开?'),
        content: const Text('你确定要离开当前页面吗?'),
        actions: <Widget>[
          TextButton(
            style: TextButton.styleFrom(textStyle: Theme.of(context).textTheme.labelLarge),
            child: const Text('取消'),
            onPressed: () {
              Navigator.of(context).pop(false); // 明确使用对话框的上下文关闭
            },
          ),
          TextButton(
            style: TextButton.styleFrom(textStyle: Theme.of(context).textTheme.labelLarge),
            child: const Text('退出'),
            onPressed: () {
              Navigator.of(context).pop(true);
            },
          ),
        ],
      );
    },
  );
}

然后直接移除延迟逻辑,正常调用页面pop即可:

return PopScope(
  canPop: false,
  onPopInvokedWithResult: (didPop, _) async {
    if (didPop) return;
    final bool shouldPop = await _showBackDialog(context) ?? false;
    if (shouldPop && context.mounted && context.canPop()) {
      context.pop();
    }
  },
);

方法2:利用帧回调等待对话框动画完成

如果不想修改对话框的弹出方式,可以使用WidgetsBinding.instance.addPostFrameCallback,在下一帧执行页面pop操作,确保对话框的关闭动画已经完成:

return PopScope(
  canPop: false,
  onPopInvokedWithResult: (didPop, _) async {
    if (didPop) return;
    final bool shouldPop = await _showBackDialog(context) ?? false;
    if (shouldPop && context.mounted) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (context.mounted && context.canPop()) {
          context.pop();
        }
      });
    }
  },
);

补充:使用GoRouter API确保路由动画正常

如果项目中依赖GoRouter的路由管理,也可以直接调用GoRouter.of(context).pop()来触发页面退出,确保路由系统正确处理过渡动画:

WidgetsBinding.instance.addPostFrameCallback((_) {
  if (context.mounted) {
    GoRouter.of(context).pop();
  }
});

内容的提问来源于stack exchange,提问作者dev2019

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 01:50:21