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

Flutter中RetryPolicy内NavigationService上下文随机为空,如何重新初始化?

解决全局Context随机为null的问题

你的问题核心在于navigatorKey.currentContext仅在Navigator组件处于活跃挂载状态时才有效,页面初始化、路由跳转中或组件被销毁时,它会随机变为null。以下是几种可行的解决思路:

方案一:使用全局Provider容器,摆脱对BuildContext的依赖

在App初始化时创建全局Provider容器,直接从容器中获取服务实例,无需依赖Context:

// 全局Provider容器
final globalProviderContainer = ProviderContainer();

// 在App入口配置
Widget build(BuildContext context) {
  return UncontrolledProviderScope(
    container: globalProviderContainer,
    child: MaterialApp(
      navigatorKey: NavigationService.navigatorKey,
      // 其他App配置
    ),
  );
}

// 修改RetryPolicy代码
class ExpiredTokenRetryPolicy extends RetryPolicy {
  @override
  int get maxRetryAttempts => 1;

  @override
  bool shouldAttemptRetryOnException(Exception reason) {
    return false;
  }

  @override
  Future<bool> shouldAttemptRetryOnResponse(ResponseData response) async {
    if (response.statusCode == 401) {
      // 直接从全局容器读取服务
      await globalProviderContainer.read(userProfileServiceProvider).initLogin();
      return true;
    }
    return false;
  }
}

方案二:等待Context可用后再执行操作

给NavigationService添加等待Context的方法,确保获取到有效Context后再调用服务:

class NavigationService {
  static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  // 新增:等待Context变为可用状态
  static Future<BuildContext> waitForValidContext() async {
    if (navigatorKey.currentContext != null) {
      return navigatorKey.currentContext!;
    }
    // 循环等待,直到Context可用
    await Future.doWhile(() {
      return Future.delayed(const Duration(milliseconds: 50), () {
        return navigatorKey.currentContext == null;
      });
    });
    return navigatorKey.currentContext!;
  }
}

// 在RetryPolicy中使用
class ExpiredTokenRetryPolicy extends RetryPolicy {
  @override
  int get maxRetryAttempts => 1;

  @override
  bool shouldAttemptRetryOnException(Exception reason) {
    return false;
  }

  @override
  Future<bool> shouldAttemptRetryOnResponse(ResponseData response) async {
    if (response.statusCode == 401) {
      final validContext = await NavigationService.waitForValidContext();
      await Provider.of<UserProfileService>(validContext, listen: false).initLogin();
      return true;
    }
    return false;
  }
}

方案三:重构认证逻辑,完全脱离Context

把initLogin方法改造成不依赖Flutter Context的独立函数,直接在网络层调用:

// 独立的认证工具类
class AuthUtil {
  static Future<void> initLogin() async {
    // 这里实现登录逻辑:比如调用登录API、更新本地Token等
    // 全程不需要Context
  }
}

// 修改RetryPolicy代码
class ExpiredTokenRetryPolicy extends RetryPolicy {
  @override
  int get maxRetryAttempts => 1;

  @override
  bool shouldAttemptRetryOnException(Exception reason) {
    return false;
  }

  @override
  Future<bool> shouldAttemptRetryOnResponse(ResponseData response) async {
    if (response.statusCode == 401) {
      await AuthUtil.initLogin();
      return true;
    }
    return false;
  }
}

建议

优先选择方案一或方案三,因为网络层逻辑(如RetryPolicy)属于非UI层,应尽量避免依赖BuildContext,从根源上消除Context为null的风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 03:32:28