How to fetch object from list inside list from Dio response - flutter

The Problem is here that I can't find a list of lists and fetch complete information of other lists from API in flutter using Dio.
void fetchAllIndicatorsList(int ModuleId) async {
try {
isLoading(true);
var indicatorslist = await DioClient.fetchAllIndicators(ModuleId);
if (indicatorslist != null) {
Indicatorslist.clear();
first list ==> IndicatorsChecklist.clear();
Indicatorslist.value = indicatorslist;
for(var i=0; Indicatorslist.length>0; i++){
==> second list IndicatorsChecklist.assignAll(Indicatorslist[i].indicators!.length.obs.toJson());
}
}
} finally {
isLoading(false);
}
}enter link description here

Check out my example of getting lists in Dio
Future getCourses() async {
String endPoint = 'api/courses';
final api = service.getApiClient();
try {
final result = await api!.get(endPoint);
final rawList = result.data["data"] as List;
final list = rawList.map((e) => Course.fromJson(e)).toList();
return list;
} on DioError catch (error) {
return error.response!.data;
}
}

Related

Flutter: _TypeError

I'm trying to get datas from api and add them a list. But at this moment, I see datas i got but I can't get it out of the function. What should i do?
function
List<dynamic> xxx = [];
#override
void initState() {
super.initState();
Future<List<dynamic>> fetchCompanies(List<dynamic> datas) async {
var response = await Dio().get(CompaniesPath().url);
if (response.statusCode == HttpStatus.ok) {
Map<String, dynamic> company = jsonDecode(response.data);
for (int i = 0; i < company['Data'].length; i++) {
datas.add(company['Data'][i]);
}
//print(datas); //=> I see datas here
} else {
throw Exception();
}
return datas;
}
print(fetchCompanies(xxx));
}
When I run print(fetchCompanies(xxx)); I got "Instance of 'Future<List<dynamic>>'". How can i get data inside fetchCompanies to my xxx list?
You're trying to print future instance of List that's why you got
Instance of Future<List>
You have to wait until function finish executing.
Catch here is you can't call wait in initState() so you have to use .then method
try this:
fetchCompanies(xxx)
.then((result) {
print("result: $result");
});
It should already work fine like it is. But you probably want to call a setState to refresh the page. Try this:
#override
void initState() {
super.initState();
Future<List<dynamic>> fetchCompanies(List<dynamic> datas) async {
var response = await Dio().get(CompaniesPath().url);
if (response.statusCode == HttpStatus.ok) {
Map<String, dynamic> company = jsonDecode(response.data);
for (int i = 0; i < company['Data'].length; i++) {
datas.add(company['Data'][i]);
}
//print(datas); //=> I see datas here
setState(() {}); // added this
} else {
throw Exception();
}
return datas;
}
print(fetchCompanies(xxx));
}

How to return catch exception in flutter

I working on error handling of api's. i want if api is crashed then it display a message of "Server is down" something like this, in UI.
I created a class where i'm creating methods of api, here in getBooks method if i modify the api url then it is printing this Exception, and i want it in UI. The problem is getBooks return type is List<Book>> so we can't return this Exception, any solution how to do this?
Exception
E/flutter (12924): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Exception
here is my api code
class BooksApi {
static Future<List<Book>> getBooks(String query) async {
try {
final url = Uri.parse(
'https://gist.githubusercontent.com/JohannesMilke/d53fbbe9a1b7e7ca2645db13b995dc6f/raw/eace0e20f86cdde3352b2d92f699b6e9dedd8c70/books.json');
final response = await http.get(url);
if (response.statusCode == 200) {
final List books = json.decode(response.body);
return books.map((json) => Book.fromJson(json)).where((book) {
final titleLower = book.title.toLowerCase();
final authorLower = book.author.toLowerCase();
final searchLower = query.toLowerCase();
return titleLower.contains(searchLower) ||
authorLower.contains(searchLower);
}).toList();
} else {
throw Exception;
}
} catch (e) {
print("e");
print(e);
}
throw Exception;
}
}
and calling it like
Future init() async {
setState(() {
isLoading = true;
});
var books = await BooksApi.getBooks(query); //this
var response = await obj.getProduct();
print(response);
setState(() => this.books = books);
setState(() {
isLoading = false;
});
}
You could handle errors with then and onError :
await BooksApi.getBooks(query).then((books) async {
setState(() => {
this.books = books;
this.isLoading = false;
})
}, onError: (error) {
// do something with error
});
or a simple try-catch (you can write try-catch clauses the same way you would in synchronous code).
See handling errors.
You can also use catchError id you don't use async/await :
BooksApi.getBooks(query).then((books) {
setState(() => {
this.books = books;
this.isLoading = false;
})
}).catchError((error, stackTrace) {
print("error is: $error");
});
See futures error handling.
Try to wrap 'var books = await BooksApi.getBooks(query)' with try and catch.
...
try {
var books = await BooksApi.getBooks(query);
} catch (e) {
// To do for UI
}
...
For api, you need to make something like this:
APIModel{
final int code;
// or a success flag
// final bool success;
final String message;
final List<Book> data;
APIModel({this.code,this.message,this.data});
}
It means, every api have its own code,message,and data filed.
When you request, you can check your code or success:
var response = await request(params);
isLoading = false;
if(response.code == 0){}
// or
if(response.success){
// do what you want
}
else {
Toast.show(response.message);
}
You can use build_runner and json_serializable.

Network foor loop async

I want to a lot of request in for loop.This is my network method.
Future postdata<T, F extends ApiResponseFromJson>({
#required T? requestmodel,
#required F? responsemodel,
#required String? path,
}) async {
try {
final response = await dio.post(path!, data: requestmodel);
print(ApiPath.API_URL + path);
if (response.data is List) {
return List<F>.from(
(response.data).map((e) => responsemodel!.fromJson(e)));
}
return responsemodel!.fromJson(response.data);
} on DioError catch (e) {
final errorMessage = DioExceptions.fromDioError(e).toString();
showSnackBarDialog("Hata", errorMessage, const Icon(Icons.error))
};
List<CarModel> carModel=[CarModel(),CarModel(),CarModel()...]
Future<ResponseModel?> postData(int index) async {
responseModel.carModel = carModel[i];
responseModel= await _networkService
.postdata<RequestModel, ResponseModel>(
requestmodel: requestModel,
responsemodel: ResponseModel(),
path: ApiPath.API_DATA_LINK);
return responseModel;
}
_allListModel() async {
for (var i = 0; i < carModel.length; i++) {
differentList.add(await postData(i));
}
I have different list and i need request list of lenght.But in for loop response always waiting the other index. But i dont need wait. Cause networkModel goes to list of lenght different API and i don't know which one result is coming to first.
I want to request in for loop and i don't need to the other index. How can i do ?
This code call all request at once, just remove await and the method then is called when request end:
_allListModel() {
for (var i = 0; i < carModel.length; i++) {
postData(i).then((value) {
differentList.add(value);
});
}
}
You should use Future.wait to wait for multiple futures. You start all of them and then you wait until all are done and add all the results to the list.
Future<void> _allListModel() async {
var futures = <Future<>>[]
for (var i = 0; i < carModel.length; i++) {
futures.add(postData(i));
}
var responses = await Future.wait(futures);
differentList.addAll(responses);
}

How to await a Map.forEach() in dart

I have a Future which returns a map. I then need to use the values of that map to await another future and then return the entire result at the end. The problem is that dart can't await async Map.forEach() methods (see this: https://stackoverflow.com/a/42467822/15782390).
Here is my code:
the debug console shows that the items printed are in the following order:
flutter: getting journal entries
flutter: about to loop through pictures
flutter: getting picture
flutter: returning entries
flutter: [[....]] (Uint8List)
Future<List<JournalEntryData>> getJournalEntries() async {
List<JournalEntryData> entries = [];
print('getting journal entries');
EncryptService encryptService = EncryptService(uid);
await journal.get().then((document) {
Map data = (document.data() as Map);
print('about to loop through pictures');
data.forEach((key, value) async {
print('getting picture');
dynamic pictures = await StorageService(uid).getPictures(key);
print('done getting image');
entries.add(JournalEntryData(
date: key,
entryText: encryptService.decrypt(value['entryText']),
feeling: value['feeling'],
pictures: pictures,
));
});
});
print('returning entries');
return entries;
}
Future getPictures(String entryID) async {
try {
final ref = storage.ref(uid).child(entryID);
List<Uint8List> pictures = [];
await ref.listAll().then((result) async {
for (var picReference in result.items) {
Uint8List? pic = await ref.child(picReference.name).getData();
if (pic == null) {
// TODO make no picture found picture
var url = Uri.parse(
'https://www.salonlfc.com/wp-content/uploads/2018/01/image-not-found-scaled-1150x647.png');
var response = await http.get(url);
pic = response.bodyBytes;
}
pictures.add(pic);
}
});
return pictures;
} catch (e) {
print(e.toString());
return e;
}
}
It's quite annoying to have to use for-loops when you need async behaviour, specially on Maps, because as the other answer shows, that requires you to iterate over entries and then take the key and value out of it like this:
for (final mapEntry in data.entries) {
final key = mapEntry.key;
final value = mapEntry.value;
...
}
Instead of that, you can write a utility extension that does the work for you:
extension AsyncMap<K, V> on Map<K, V> {
Future<void> forEachAsync(FutureOr<void> Function(K, V) fun) async {
for (var value in entries) {
final k = value.key;
final v = value.value;
await fun(k, v);
}
}
}
Then, you can use that like this:
await data.forEachAsync((key, value) async {
...
});
Much better.
Don't mix the use of then and await since it get rather confusing and things are no longer being executed as you think.
Also, the use of forEach method should really not be used for complicated logic like what you are doing. Instead, use the for-each loop. I have tried rewrite getJournalEntries here:
Future<List<JournalEntryData>> getJournalEntries() async {
List<JournalEntryData> entries = [];
print('getting journal entries');
EncryptService encryptService = EncryptService(uid);
final document = await journal.get();
Map data = (document.data() as Map);
print('about to loop through pictures');
for (final mapEntry in data.entries) {
final key = mapEntry.key;
final value = mapEntry.value;
print('getting picture');
dynamic pictures = await StorageService(uid).getPictures(key);
print('done getting image');
entries.add(JournalEntryData(
date: key,
entryText: encryptService.decrypt(value['entryText']),
feeling: value['feeling'],
pictures: pictures,
));
}
print('returning entries');
return entries;
}
And getPictures here. I have only removed the use of then here.
Future getPictures(String entryID) async {
try {
final ref = storage.ref(uid).child(entryID);
List<Uint8List> pictures = [];
final result = await ref.listAll();
for (var picReference in result.items) {
Uint8List? pic = await ref.child(picReference.name).getData();
if (pic == null) {
// TODO make no picture found picture
var url = Uri.parse(
'https://www.salonlfc.com/wp-content/uploads/2018/01/image-not-found-scaled-1150x647.png');
var response = await http.get(url);
pic = response.bodyBytes;
}
pictures.add(pic);
}
return pictures;
} catch (e) {
print(e.toString());
return e;
}
}

Cannot get List data from SharedPreferences using FutureBuilder call

I have a String List in my shared preferences that's meant to store account numbers which can then be used to make a call from the API:
List<String> accountList = [];
Future _getListData() async {
SharedPreferences myPrefs = await SharedPreferences.getInstance();
for(var i = 0; i < myPrefs.getStringList('accounts').length; i++){
accountList.add(myPrefs.getStringList('accounts')[i]);
}
//prints values from shared preferences
print(accountList);
}
Future<List<dynamic>> fetchData() async {
_getListData();
//prints an empty list
print(accountList);
try {
if (accountList == null) {
var result = await http.get(apiUrl);
List<dynamic> accountInfo = (json.decode(result.body));
return accountInfo;
} else {
List<dynamic> accountInfo = [];
for(var i = 0; i < accountList.length; i++){
var result =
await http.get(apiUrl + "/api/Data/GetCustomer?accntnum=" + accountList[i]);
accountInfo.add(json.decode(result.body));
}
return accountInfo;
}
} catch (e) {
print(e);
}
}
When calling the Future function "fetchData()" from my FutureBuilder in the widget it returns an empty list. However within the scope of the "_getListData()" function it prints the list witht he appropriate values. How can I make it such that "fetchData()" gets the intended list?
Just add an await before _getListData() here:
Future<List<dynamic>> fetchData() async {
await _getListData();
//prints an empty list
print(accountList);