Search code examples
flutterrestandroid-studioretrofitdio

How can I handle token saving in header using interceptor flutter?


have a Dart project using the Injectable package for dependency injection. I'm facing a circular dependency issue between three classes: TokenInterceptor, AuthRepository, and ApiDatasource, which leads to a stack overflow error during the initialization of dependencies.

The code structure is as follows:

register_module.dart:

  @module
  abstract class RegisterModule {
    @lazySingleton
    Dio dio() => Dio(
          BaseOptions(baseUrl: Const.baseUrl),
        )..interceptors.addAll(
            [
              LogInterceptor(),
              TokenInterceptor(authRepository: ),
            ],
          );
  }

api_datasource.dart:

part 'api_datasource.g.dart';

@lazySingleton
@RestApi(baseUrl: Const.baseUrl)
abstract class ApiDatasource {

  @POST('/identity/Auth/userRegister')
  Future<void> registerUser(@Body() RegisterModel registerModel);

  @POST('/identity/Auth/userLogin')
  Future<void> loginUser(
    @Body() LoginModel loginModel,
  );

  @GET('/identity/Auth/resetPassword')
  Future<void> resetPassword(
    @Body() ResetPasswordModel resetPasswordModel,
  );

  @GET('/identity/Auth/setNewPassword/{token}')
  Future<void> setNewPassword(
    @Path() String token,
    @Body() SetNewPasswordModel setNewPasswordModel,
  );

  @GET('/pet/advertisements/shelters/{longitude}/{latitude}')
  Future<List<PetModel>> getAdvertisement(
    @Path('longitude') double longitude,
    @Path('latitude') double latitude,
    @Query('type') String type,
    @Query('gender') String gender,
    @Query('pageNumber') int pageNumber,
    @Query('pageSize') int pageSize,
  );

  @GET('/pet/advertisements/shelters/{petId}/{longitude}/{latitude}')
  Future<List<PetCardModel>> getDetailsAdvertisement(
    @Path('longitude') double longitude,
    @Path('latitude') double latitude,
    @Path('petId') double petId,
  );
}

token_interceptor.dart:

 class TokenInterceptor extends Interceptor {
  TokenInterceptor({required this.authRepository});
  final AuthRepository authRepository;
  @override
  Future<void> onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    final String? token = await authRepository.accessToken;
    if (token != null) {
      options.headers['Authorization'] = 'Bearer $token';
    }
    handler.next(options);
  }
}

auth_repository.dart:

       const _accessTokenKey = 'access_token';

    @lazySingleton
    class AuthRepository {
      AuthRepository(this.apiDatasource);

      final LappkaApiDatasource apiDatasource;

      String? _accessToken;

      FutureOr<String?> get accessToken async {
        if (_accessToken != null) {
          return _accessToken;
        }
        return readAccessToken().then((token) {
          _accessToken = token;
          return token;
        });
      }

      Future<String?> readAccessToken() async {
        try {
          const secureStorage = FlutterSecureStorage();
          return secureStorage.read(key: _accessTokenKey);
        } catch (e) {
          return null;
        }
      }


  Future<bool> saveToken(String token) async {
    try {
      const secureStorage = FlutterSecureStorage();
      await secureStorage.write(key: _accessTokenKey, value: token);
      _accessToken = token;
      return true;
    } catch (e) {
      return false;
    }
  }

  Future<void> registerUser(RegisterModel registerModel) async {
    await apiDatasource.registerUser(registerModel);
  }

  Future<void> loginUser(LoginModel loginModel) async {
    await apiDatasource.loginUser(loginModel);
  }

  Future<void> resetPassword(ResetPasswordModel resetPasswordModel) async {
    await apiDatasource.resetPassword(resetPasswordModel);
  }

  Future<void> setNewPassword(
      String token, SetNewPasswordModel setNewPasswordModel) async {
    await apiDatasource.setNewPassword(token, setNewPasswordModel);
  }
}

The TokenInterceptor depends on AuthRepository, and AuthRepository depends on ApiDatasource, which in turn depends on TokenInterceptor, creating a circular dependency loop.

I have tried using Factory injection for TokenInterceptor to defer its instantiation, but I still encounter the stack overflow issue.

How can I resolve this circular dependency and prevent the stack overflow during the dependency injection process?

Thank you for your help!


Solution

  • The solution is pretty simple. Just make another repository like token_repository.