Flutter - Best way to request Multiple APIs simultaneously - flutter

I have two URLs, and I am using the fetchData() function to parse the json.
Future<Iterable> fetchData() async {
var response = await http.get(firstUrl);
var listOne = List<Article>();
if (response.statusCode == 200) {
var notesJson = json.decode(response.body);
var bodyList = notesJson['items'];
for (var i in bodyList) {
listOne.add(Article.fromJson(i));
}
}
var resp = await http.get(secondUrl);
var listTwo = List<Article>();
if (resp.statusCode == 200) {
var notesJson = json.decode(resp.body);
var bodyList = notesJson['items'];
for (var i in bodyList) {
listTwo.add(Article.fromJson(i));
}
}
var newL = [...listOne, ...listTwo];
return newL;
}
I find this redundant. I want to know if this is the right approach, or can I optimize it? Since I am querying two URLs, should I be using compute() instead?

Flutter's compute spawns a whole other Isolate (thread-like things in Dart) and that's pretty resource-intensive for just waiting on a network request.
Gladly, Dart is event-loop-based, so you can wait on both requests simultaneously by simply wrapping both network request Futures in a call to Future.wait.
For more information about Dart's event loop, you might want to check out the explanatory video about Futures on Flutter's YouTube channel.
Future<List<Article>> fetchData() async {
var responses = await Future.wait([
http.get(firstUrl),
http.get(secondUrl),
]);
return <Article>[
..._getArticlesFromResponse(responses[0]),
..._getArticlesFromResponse(responses[1]),
];
}
List<Article> _getArticlesFromResponse(http.Response response) {
return [
if (response.statusCode == 200)
for (var i in json.decode(response.body)['items'])
Article.fromJson(i),
];
}

if you have dynamic list you can use Future.forEach method;
for example:
var list = ["https://first.api.url",
"https://second.api.url",
"https://third.api.url"];
void makeMultipleRequests(){
await Future.forEach(list, (url) async{
await fetchData(url);
});
}
Future<Iterable> fetchData(String url) async {
var response = await http.get(url);
print(response.body);
}

You can use Dio package.
response = await Future.wait([dio.post('/info'), dio.get('/token')]);

Related

Flutter await does not await until return of function

I tried to find an answer but my problem is still there.
In my asynchronous upload function I return at the and the generated name of the image, which I want to use to make my database request.
This is my upload function:
Future<String> upload(File imageFile) async {
var stream =
new http.ByteStream(DelegatingStream.typed(imageFile.openRead()));
// get file length
var length = await imageFile.length();
var uri = Uri.parse("http://localhost:8080/upload");
var request = new http.MultipartRequest("POST", uri);
var multipartFile = new http.MultipartFile('file', stream, length,
filename: basename(imageFile.path));
request.files.add(multipartFile);
var response = await request.send();
print(response.statusCode);
var createdFileName = "";
response.stream.transform(utf8.decoder).listen((value) {
createdFileName = value;
print(createdFileName);
});
return createdFileName;
}
I call it like this:
List createdFileNames = [];
for (var e in imagefiles) {
createdFileNames.add(await upload(File(e)));
}
I don't know why, but the createdFileNames are ["",""], but the upload gives as result the right name. In debug mode I can see, that the loop does not wait until the upload has finished.
Do you have any suggestions?
Thank you very much!
response.stream.transform(utf8.decoder).listen((value) {
createdFileName = value;
print(createdFileName);
});
This part in your function is asynchronous, it uses a callback.
But you don't wait for it to finish in any form. You just continue to return the createdFileName, that by that time most likely has not been filled.
I don't know what your stream looks like, if you only need the first value, you could await that instead of listening:
createdFileName = await response.stream.transform(utf8.decoder).first;
Replace
response.stream.transform(utf8.decoder).listen((value) {
createdFileName = value;
print(createdFileName);
});
with
createdFileName=await response.stream.bytesToString();
change code
for (var e in imagefiles) {
upload(File(e)).then((value) => createdFileNames.add(value));
}

Replace "map" method to traditional loops in dart when fetching data from API

I was wondering if there is a different approach more efficient to include data from a json API to a simple list.
As I read in some posts, map method is the most time/resource consuming in comparation with the traditional for/while loop in Dart.
Currently I use this snippet to fetch my data:
Future<List<dynamic>> fetchData(url) async {
var client = http.Client();
final response = await client.get(Uri.parse(url));
await Future.delayed(Duration(seconds:2));
if (response.statusCode == 200) {
var jsonDecoded = json.decode(response.body);
BreedList = jsonDecoded.map((data) => DogClass.fromJson(data)).toList();
glossarList = BreedList;
return BreedList;
} else {
throw Exception('Failed to load data');
}
}
I tried this approach:
Future<List<dynamic>> fetchDataFor(url) async {
var client = http.Client();
final response = await client.get(Uri.parse(url));
await Future.delayed(Duration(seconds:2));
if (response.statusCode == 200) {
var jsonDecoded = json.decode(response.body);
for (var k in jsonDecoded.keys){
BreedList.add({jsonDecoded[k]});
}
return BreedList;
} else {
throw Exception('Failed to load data');
}
}
But it returns the error: Class List has no instance getter 'keys'.
So, what would be the equivalent for the "map" method ?
You can use collection-for to perform a straightforward transformation of .map calls.
var result = iterable.map((element) => transform(element)).toList();
can be replaced with:
var result = [for (var element in iterable) transform(element)];
So in your case:
BreedList = jsonDecoded.map((data) => DogClass.fromJson(data)).toList();
can become:
BreedList = [for (var data in jsonDecoded) DogClass.fromJson(data)];

Flutter await for another method complete

I want to check if new update is available for my application or not. if update is available redirect user to UpdateScreen and if update is not available get the user info and redirect to HomeScreen
_check() async {
await _checkForUpdate();
await _getUserData(token);
}
_checkForUpdate() async {
print('check for update');
var url = Uri.parse(Endpoints.mainData);
var response = await http.get(url);
var jsonResponse = convert.jsonDecode(response.body);
var data = jsonResponse['data'];
int lastVersionCode = data['lastVersionCode'];
if(lastVersionCode > Data.versionCode){
redirectToScreen(context, UpdateScreen());
}
}
_getUserData(String token) async {
print('get user data');
var url = Uri.parse(Endpoints.accountInfo + '/?token=' + token);
var response = await http.get(url);
var jsonResponse = convert.jsonDecode(response.body);
var data = jsonResponse['data'];
//setup user data in my app
redirectToScreen(context, HomeScreen());
When I run my application two methods( _checkForUpdate, _getUserData) get fired and in output I the get following message that i printed:
check for update
get user data
and i see Update screen for 1 second and then user is redirect to HomeScreen.
i want to skip running the other codes after _checkForUpdate redirect user to UpdateScreen
return a bool whether there is an update available and use it to skip other methods:
_check() async {
bool needsUpdate = await _checkForUpdate();
if (!needsUpdate)
await _getUserData(token);
}
Future<bool> _checkForUpdate() async {
print('check for update');
var url = Uri.parse(Endpoints.mainData);
var response = await http.get(url);
var jsonResponse = convert.jsonDecode(response.body);
var data = jsonResponse['data'];
int lastVersionCode = data['lastVersionCode'];
if (lastVersionCode > Data.versionCode) {
redirectToScreen(context, UpdateScreen());
return true;
}
return false;
}

Flutter : How to add more json data to existing Model Class?

I have a scenario where the following function is called again and again whenever the user hits the "Load More" button.
The problem I'm facing is, that it replaces previously loaded data with a new one. Instead, it should add to the bottom of the Listview.Builder
Future fetchData() async{
var url = "url_goes_here";
final response = await http.get(url);
if (response.statusCode == 200) {
var resBody = jsonDecode(response.body);
var data = resBody['data'] as List;
if (data.isNotEmpty) {
setState(() {
listVariable = data
.map<ModelClass>((json) => ModelClass.fromJson(json))
.toList();
});
}
}
}
List<ModelClass> listVariable =List<ModelClass>(); //describe the object that way.
--------and---------
data.map<ModelClass>((json) {
listVariable.add(ModelClass.fromJson(jsonn));
} )).toList();
You should add received data to your listVariable, not assign a new value. Try this code:
final listVariable = <ModelClass>[];
...
Future fetchData() async {
var url = "url_goes_here";
final response = await http.get(url);
if (response.statusCode == 200) {
var resBody = jsonDecode(response.body);
var data = resBody['data'] as List;
if (data.isNotEmpty) {
final list = data.map<ModelClass>((json) => ModelClass.fromJson(json));
setState(() {
listVariable.addAll(list); // HERE: addAll() instead of assignment
});
}
}
}
I was able to figure out answer myself.
setState(() {
listVariable.addAll(data
.map<ModelClass>((json) => ModelClass.fromJson(json))
.toList();
}));
#Mol0ko and #hasan karaman both are right but #Mol0ko
Makes better sense when you have a set of data to addAll to existing data.

Is there any way to iterate over future<list> in flutter

I would like to iterate over the the Future<list> but get errors if I try using for loops or if I try setting it as List.
Is there any other way of looping over each element in the list:
Future<List> getDailyTask(DateTime date) async {
var params = {'date': date.toIso8601String()};
Uri uri = Uri.parse('URL');
final newURI = uri.replace(queryParameters: params);
http.Response response = await http.get(
newURI,
headers: {"Accept": "application/json"},
);
List foo = json.decode(response.body);
return foo;
}
You can't iterate directly on future but you can wait for the future to complete and then iterate on the List.
You can use either of these methods, depending upon your use-case.
List dailyTaskList = await getDailyTask(DateTime.now());
// Now you can iterate on this list.
for (var task in dailyTaskList) {
// do something
}
or
getDailyTask(DateTime.now()).then((dailyTaskList) {
for (var task in dailyTaskList) {
// do something
}
});