Search code examples
flutterdartinterceptor

Hot to pass BuildContext inside the RetryPolicy Interceptor


I need to create an interceptor that whenever my API response is 401, I need to refresh the token with the refreshToken. And if something goes wrong, I need to redirect the user to the LoginPage.

But I'm having some difficulties on passing the BuildContext inside the refreshToken method... I'm using the http_interceptor dependency.

class AuthedApiClient {
  http.Client client = InterceptedClient.build(
    interceptors: [AuthorizationInterceptor()],
    requestTimeout: const Duration(seconds: 10),
  );

 static Future<void> refreshToken(BuildContext context) async {
    LoginService loginService = LoginService();
    try {
      loginService.refreshLogin();
    } catch (e) {
      if (e is RefreshTokenException) {
        Fluttertoast.showToast(
          msg: e.message,
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.BOTTOM,
          timeInSecForIosWeb: 1,
          textColor: Colors.black,
          backgroundColor: ThemeColors.errorToastMessage,
          fontSize: 12.0,
        );
      }
      Navigator.pushReplacementNamed(context, '/');
    }
  }
}
class AuthorizationInterceptor implements InterceptorContract {
  Logger logger = Logger();

  @override
  Future<RequestData> interceptRequest({required RequestData data}) async {
    logger.v(
        "Requisição para: ${data.baseUrl}\nCabeçalhos: ${data.headers}\nCorpo: ${data.body}");
    try {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      var token = prefs.getString('token');
      data.headers.clear();
      data.headers['Authorization'] = 'Bearer ${token!}';
      data.headers['Content-type'] = 'application/json';
      data.headers['Client'] = 'CustomerApp';
    } catch (e) {
      logger.e(e);
    }
    return data;
  }

  @override
  Future<ResponseData> interceptResponse({required ResponseData data}) async {
    return data;
  }
}

class ExpiredTokenRetryPolicy extends RetryPolicy {
  @override
  Future<bool> shouldAttemptRetryOnResponse(ResponseData response) async {
    if (response.statusCode == 401) {
      await AuthedApiClient
          .refreshToken(); //I need to somehow pass the context to this guy here...
      return true;
    }
    return false;
  }
}

Solution

  • Sometimes we might not be able to get current context in our app wherever we need, in those cases, We should consider using Global Context.

    For using Global Context, What we need to do is:

    create a class like this:

    class AppSettings {
      static GlobalKey<NavigatorState> navigatorState = GlobalKey<NavigatorState>();
    }
    

    Now, inside your MaterialApp give navigatorKey as:

    navigatorKey: AppSettings.navigatorState,
    

    Now, to get the BuildContext inside your refreshToken function, you don't need to define context as parameter.

    Replace this Navigator.pushReplacementNamed(context, '/'); by:

    Navigator.pushReplacementNamed(AppSettings.navigatorState.currentContext!, '/');
    

    And you're done.