In Flutter, I am trying to write a function that periodically calls an API until a URL is returned, then passes that URL to another function. How the API works is that on the first call, it responds with acknowledgement that a request is made. Then, after a period of time, it generates a URL that can be clicked to export files.
However, my current error is that the function ends before the polling is finished and the URL is assigned to the exportURL
variable I want to return to the other function.
The error I get is [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DioException [unknown]: null
, Error: Invalid argument(s): No host specified in URI
as the URL is not passed correctly to my other function.
Below is my function to poll the API.
Future<String> _pollingForExport(String url, Options options) async{
String exportUrl = '';
Timer.periodic(const Duration(seconds: 3), (timer) async {
//Call API with post method again
final responseRequested = await dio.post(
url,
options: options,
);
print('In polling mechanism, response requested is ${responseRequested.data}');
//If successful request and the response is a url String, cancel the timer and exit Polling
//We know if the response is a link if it just contains a string, which is the url
if(responseRequested.statusCode == 200 && responseRequested.data is String){
print('Link should be found. response is ${responseRequested.data}');
exportUrl = responseRequested.data as String;
timer.cancel();
}
//Else if there is a bad request, tell user error and return function
else if (responseRequested.statusCode != 200){
print('Error when polling API, bad request made');
timer.cancel();
return ; //Not sure if I return here, but seems right
}
});
return exportUrl;
}
Below is the code where the url is returned by the polling function. It is passed to dio.download
to export the file.
String exportUrl = await _pollingForExport(url, options);
String downloadPath = (await getApplicationDocumentsDirectory()).path;
final responseExport = await dio.download(exportUrl, downloadPath + '.txt', options: options);
I have tried reading documentation on Flutter timer and async functions. I also tried not using a function and just sticking the timer where I need it, but that didn't work either.
Any help is appreciated, thank you!
the problem is that your function returns immediately, it does not "await" the timer.
Future<String> _pollingForExport(String url, Options options) async{
String exportUrl = '';
//This is not awaited
Timer.periodic(const Duration(seconds: 3), (timer) async {
//Call API with post method again
final responseRequested = await dio.post(
url,
options: options,
);
print('In polling mechanism, response requested is ${responseRequested.data}');
//If successful request and the response is a url String, cancel the timer and exit Polling
//We know if the response is a link if it just contains a string, which is the url
if(responseRequested.statusCode == 200 && responseRequested.data is String){
print('Link should be found. response is ${responseRequested.data}');
exportUrl = responseRequested.data as String;
timer.cancel();
}
//Else if there is a bad request, tell user error and return function
else if (responseRequested.statusCode != 200){
print('Error when polling API, bad request made');
timer.cancel();
return ; //Not sure if I return here, but seems right
}
});
//this returns immediately
return exportUrl;
}
Maybe a completer would be better
Future<String> _pollingForExport(String url, Options options) async{
final exportUrlCompleter = Completer<String>();
//This is not awaited
Timer.periodic(const Duration(seconds: 3), (timer) async {
//Call API with post method again
final responseRequested = await dio.post(
url,
options: options,
);
print('In polling mechanism, response requested is ${responseRequested.data}');
//If successful request and the response is a url String, cancel the timer and exit Polling
//We know if the response is a link if it just contains a string, which is the url
if(responseRequested.statusCode == 200 && responseRequested.data is String){
print('Link should be found. response is ${responseRequested.data}');
final exportUrl = responseRequested.data as String;
timer.cancel();
exportUrlCompleter.complete(exportUrl);
}
//Else if there is a bad request, tell user error and return function
else if (responseRequested.statusCode != 200){
print('Error when polling API, bad request made');
exportUrlCompleter.completeError("Exception occurred");
timer.cancel();
}
});
//this returns immediately
return exportUrlCompleter.future;
}