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

Flutter异步登录+模态导航报错:Bad state: Future already completed

解决GoRouter中异步跳转登录页导致的Future已完成报错

应用场景

  • 设置页:包含触发登录流程的按钮
  • 登录页:提供用户输入账号凭证的界面

导航流程

  1. 在设置页点击登录按钮,通过showModalBottomSheet(配置useRootNavigator: true)弹出模态框
  2. 模态框内点击登录按钮,通过LoginRoute().push(context)跳转到登录页
  3. 用户在登录页输入凭证后点击登录按钮
  4. 登录成功时需同时关闭登录页和模态框;登录失败则仅关闭登录页,保留模态框

额外配置

LoginRoute通过全局导航Key确保路由显示在底部导航栏上方:

static final GlobalKey<NavigatorState> $parentNavigatorKey = rootNavigatorKey;

问题描述

登录页的登录操作是异步调用远程API,为了等待登录结果(返回true表示成功),将模态框中跳转登录的方法设为async,并执行:

bool? result = await LoginRoute().push<bool>(context);

但此时触发如下报错:

Unhandled Exception: Bad state: Future already completed

堆栈跟踪:

E/flutter ( 7668): #0      _AsyncCompleter.complete (dart:async/future_impl.dart:84:31)
E/flutter ( 7668): #1      ImperativeRouteMatch.complete (package:go_router/src/match.dart:456:15)
E/flutter ( 7668): #2      GoRouterDelegate._completeRouteMatch (package:go_router/src/delegate.dart:171:14)
E/flutter ( 7668): #3      GoRouterDelegate._handlePopPageWithRouteMatch.<anonymous closure> (package:go_router/src/delegate.dart:151:9)

目前使用WidgetsBinding.instance.addPostFrameCallback((_) {context.pop();})可正常运行,但并非最优方案,需更合理的解决方式。


可行解决方案

方案1:改用then链式调用替代async/await

避免异步上下文持有冲突,通过then处理登录返回结果:

// 模态框内的跳转登录方法
void navigateToLogin(BuildContext context) {
  LoginRoute().push<bool>(context).then((bool? result) {
    if (result == true) {
      // 使用rootNavigator上下文关闭模态框
      Navigator.of(context, rootNavigator: true).pop();
    }
  });
}

方案2:分离模态框与登录页的导航上下文

打开模态框时记录其上下文,后续关闭模态框时直接使用该上下文:

// 设置页中打开模态框的代码
void showLoginModal(BuildContext context) async {
  final modalContext = await showModalBottomSheet(
    context: context,
    useRootNavigator: true,
    builder: (context) => AccountPage(),
  );
}

// AccountPage中处理登录跳转的方法
void onLoginTap(BuildContext modalContext) {
  LoginRoute().push<bool>(context).then((result) {
    if (result == true) {
      Navigator.of(modalContext).pop();
    }
  });
}

方案3:在登录页直接回退到目标路由层级

登录成功时,通过popUntil一次性回退到设置页,自动关闭中间的登录页和模态框:

// 登录页的登录成功逻辑
if (loginSuccess) {
  // 回退到设置页(需确保设置页路由名称为'SettingsPage')
  context.popUntil((route) => route.settings.name == 'SettingsPage');
} else {
  context.pop();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 08:42:12