I am trying to update my header when I retry my API upon 401 response I am able to retry but my header is taking the previous value but not updated value
following is my changeNotifierProxyProvider
ChangeNotifierProxyProvider<Auth, ApiCalls>(
create: (_) => ApiCalls(),
update: (context, auth, previous) => previous!..updates(auth)),
following is my auth class where I am updating my token
class Auth extends ChangeNotifier {
String? accessToken;
DateTime? accessTokenExpiryDate;
String? get token {
if (accessTokenExpiryDate != null &&
accessTokenExpiryDate!.isAfter(DateTime.now()) &&
accessToken != null) {
return accessToken;
} else if (accessTokenExpiryDate!.isBefore(DateTime.now())) {
return accessToken;
}
return null;
}
Future<void> restoreAccessToken() async {
final url = '${Ninecabsapi().urlHost}${Ninecabsapi().login}/$sessionId';
var response = await http.patch(
Uri.parse(url),
headers: {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': accessToken!
},
body: json.encode(
{"refresh_token": refreshtoken},
),
);
var userDetails = json.decode(response.body);
if (response.statusCode == 401) {
print(userDetails['messages']);
}
sessionId = userDetails['data']['session_id'];
accessToken = userDetails['data']['access_token'];
accessTokenExpiryDate = DateTime.now().add(
Duration(seconds: userDetails['data']['access_token_expiry']),
);
refreshToken = userDetails['data']['refresh_token'];
refreshTokenExpiryDate = DateTime.now().add(
Duration(seconds: userDetails['data']['refresh_token_expiry']),
);
final userData = json.encode({
'sessionId': sessionId,
'refreshToken': refreshToken,
'refreshExpiry': refreshTokenExpiryDate!.toIso8601String(),
'accessToken': accessToken,
'accessTokenExpiry': accessTokenExpiryDate!.toIso8601String()
});
notifyListeners();
final prefs = await SharedPreferences.getInstance();
prefs.setString('userData', userData);
reset();
}
}
following is my class where I wanted to call an api with updated token
class ApiCalls extends ChangeNotifier {
String? token;
void updates(Auth token) {
this.token = token.token;
}
Future<void> sample(BuildContext context, String s) async {
print('sample:$token');
var data = {"s": s, "filename":s};
Map<String, String> obj = {"attributes": json.encode(data).toString()};
var flutterFunctions =
Provider.of<FlutterFunctions>(context, listen: false);
final url =su().urlHost + su().getvehicle;
try {
loading();
var response = http.MultipartRequest("POST", Uri.parse(url))
..files.add(await http.MultipartFile.fromPath(
"imagefile", flutterFunctions.imageFile!.path,
contentType: MediaType("image", "jpg")))
..headers['Authorization'] = token!
..fields.addAll(obj);
final client = RetryClient(
http.Client(),
retries: 1,
when: (response) {
return response.statusCode == 401 ? true : false;
},
onRetry: (req, res, retryCount) async {
//print('retry started $token');
if (retryCount == 0 && res?.statusCode == 401) {
// Only this block can run (once) until done
await Provider.of<Auth>(context, listen: false)
.restoreAccessToken();
req.headers['Authorization'] = token!;
// extractData['accessToken']
print('retry started ${req.headers['Authorization']}');
//req.headers.clear();
}
},
);
final send = await client.send(response);
final res = await http.Response.fromStream(send);
var messages = json.decode(res.body);
notifyListeners();
} catch (e) {
print(e);
}
}
}
I had tried different methods but no one worked I had tried to call token directly from shared preference but it dint work I am attaching response please check
The problem is that notifyListeners
schedules the notification for the next frame. Internally, what notifyListeners
simply does is to mark the widget as dirty (markNeedsBuild
) so the listeners get rebuilt on the next frame only. We can say that notifyListeners
is asynchronous.
So, even though you await Auth.restoreAccessToken
in the ApiCalls.sample
, only in the next frame does the call to ApiCalls.updates
occurs. But this await
is longer done and the token is still the same as before for the retry.
To fix it just return the token from the Auth.restoreAccessToken
in a Future
. Something like the below code snippet:
token = await Provider.of<Auth>(context, listen: false)
.restoreAccessToken();
And there is no need to listen to Auth
updates anymore.