List responseJson;
List eventDetails = [];
Future<String> fetchPost() async {
final response = await http.get(
"https..",
headers: {
HttpHeaders.AUTHORIZATION:
"Bearer ..."
});
for(var i = 0; i< (responseJson?.length ?? 0);i++) {
final eventDetailsResponse = await http.get(
"https:.." + responseJson[i]["id"].toString(),
headers: {
HttpHeaders.AUTHORIZATION:
"Bearer .."
});
eventDetails.add(json.decode(eventDetailsResponse.body));
}
this.setState(() {
responseJson = json.decode(response.body);
print(eventDetails);
print(responseJson);
});
Hi there! I have an array defined, I filled the array in with elements, each element is the result of an API call, and then I use the function this.setState to trigger the list view to update, on the first run eventDetails is empty, however on the second run it gets filled in with data, how can I make it await?
Thank you in advance.
Are you trying to populate your view from a future?
If that's the case, have a look at FutureBuilder.
Related
In my Flutter Web application I am retrieving values from the map timeslots in Firestore.
This is what the data looks like:
But, instead of retrieving the whole list of values, I get a truncated list like this:
[Mo-Washing-(09:00-10:00, 10:00-11:00, 11:00-12:00, ..., 20:00-21:00, 21:00-22:00)]
Below I have included the 2 functions responsible for retrieving the data and adding it to the list object
static List object = [];
static Map<String, dynamic> timeDetails = {};
static Map<String, dynamic> userDetails = {};
checkExists(docuID) async {
return await firestore()
.collection('environments')
.doc(docuID)
.get()
.then((val) {
userDetails.addAll(val.data());
}).whenComplete(() async {
fs.DocumentSnapshot snapShot = await firestore()
.collection('environments')
.doc(docuID)
.collection('Washing')
.doc('monday')
.get();
if (snapShot == null || !snapShot.exists) {
print('does not exist');
} else {
await getData(docuID, 'Washing');
}
setState(() {});
});
}
getData(docuID, machineName) async {
return await firestore()
.collection('environments')
.doc(docuID)
.collection(machineName)
.doc('monday')
.get()
.then((val) {
timeDetails.addAll(val.data());
}).whenComplete(() {
object.add('Mo-$machineName-${timeDetails['timeslots'].values}');
print(object);
setState(() {});
});
}
This also happens in debugPrint. Would anyone know why this is happening and how I could solve it? Any help on this would be appreciated!
Neither the workaround as mentioned on Github nor debugPrint worked for me, but I managed to solve this by adding .toList() to my getData function:
getData(docuID, machineName) async {
return await firestore()
.collection('environments')
.doc(docuID)
.collection(machineName)
.doc('monday')
.get()
.then((val) {
timeDetails.addAll(val.data());
}).whenComplete(() {
//toList() is added here to .add
object.add('Mo-$machineName-${timeDetails['timeslots'].values.toList()}');
print(object);
setState(() {});
});
}
Output:
[Mo-Washing-[09:00-10:00, 10:00-11:00, 11:00-12:00, 12:00-13:00, 13:00-14:00, 14:00-15:00, 15:00-16:00, 16:00-17:00, 17:00-18:00, 18:00-19:00, 19:00-20:00, 20:00-21:00, 21:00-22:00]
I store all API data to cache. some APIs have more than 10000 data. Postman response time is within one second. but in application very slow to navigate to next page. I used this code:
onPressed: () async {
...
}
else {
var token = Token(
id: 1,
token: tokens,
refreshToken: model.data.refreshToken,
);
await storeRegister(_url,tokens);
await storeEquipmentReg(_url,tokens);
await storeSyncLogin(_url,tokens);
await HelperDefCatMaster().deleteDefCatMaster();
await storeDefCatMaster(_url,tokens);
await HelperDefRegisterCat().deleteDefRegisterCat();
await storeDefRegisterCat(_url,tokens);
await HelperDefCatMaster().deleteDefCatRelation();
await storeDefCatRelation(_url,tokens);
await HelperDefCatMaster().deleteWoDescription();
await storeWoDescription(_url,tokens);
await HelperDefCatMaster().deleteCategoryDefect();
await storeCategoryDefect(_url,tokens);
await storeWorkSource(_url,tokens);
await storeWorkTypes(_url,tokens);
await storePriorities(_url,tokens);
await Helper().insert(token);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ListPage(model.data.token)));
}
storePriorities function look like,
storePriorities(String url, String token) async {
final response = await http.get(
'${url}/v1.0/Priorities',
headers: {'Authorization': 'Bearer ${token}'},
);
final jsonResponse = json.decode(response.body);
Priorities model = Priorities.fromJson(jsonResponse);
int length = model.data.length;
for (int i = 0; i < length; i++) {
var data = DataPriorities(
i: model.data[i].i,
d: model.data[i].d,
);
await HelperDefCatMaster().insertPriorities(data);
}
}
I have given the first answer that suggests to use await only when it's needed.
Well if you are inserting too much data in SQLite I assume that you might be using something like this:
for (int i = 0; i <= 1000; i++) {
db.insert('table_name', dataObject.toMap());
}
Well this will do a lot many transactions at a time and it will consume a lot's of your time.
Change this to something like this and it will increase the speed of inserting data:
Batch batch = db.batch();
for (int i = 0; i <= 1000; i++) {
batch.insert('table_name', dataObject.toMap());
}
await batch.commit();
What we are doing here is that, in single transaction we are doing multiple inserts at a time.
I made this change in my demo project where I was inserting 1000 row at a time and results were great. db.insert when called 1000 times took 7 secs where as batch.insert took less than 1 sec for inserting same amount of data.
If you optimize your code with this solution and use await when needed you should not face any problem on UI. Let me know if this helps.
You are using await keyword to fetch data from SQLite.
And you are fetching a lots of data.
This will make data fetching synchronous, and will affect your UI.
If it is convenient for your use-case to fetch data asynchronously then you can use the following way:
Change :
await Helper().insert(token);
Navigator.push(
context,MaterialPageRoute(builder: (context) => ListPage(model.data.token)));
to :
Helper().insert(token).then((onValue) {
Navigator.push(context,MaterialPageRoute(
builder: (context) => ListPage(model.data.token),
),
);
}
Note: Make your insert method return Future<'token's return type'>
Now use this way for all other await calls.
I have a multiword String that I'd like to convert to a GET request parameter.
I have an API endpoint /search that takes in the parameter query. Now typically your request would look like http://host/search?query=Hello+World.
I have a String Hello World that I'd like to convert to this URL encoded parameter.
Ofcourse, I could just write the logic to break it into words and add a + in between but I was wondering if the URI class could help with this
I'm using Dart's httpClient to make a request.
Future<String> _getJsonData(String queryToSearch) async {
List data = new List();
var httpClient = new HttpClient();
var request = await httpClient.getUrl(Uri.parse(
config['API_ENDPOINT'] + '/search?query=' +
queryToSearch));
var response = await request.close();
if (response.statusCode == HttpStatus.OK) {
var jsonString = await response.transform(utf8.decoder).join();
data = json.decode(jsonString);
print(data[0]);
return data[0].toString();
} else {
return "{}";
}
}
Essentially, need to encode queryToSearch as the URL parameter.
You can use Uri.http(s) which wrap everythings (query, host, and path) together and encode them accordingly.
final uri = new Uri.http(config['API_ENDPOINT'], '/search', {"query": queryToSearch});
The Uri class provides methods for that
https://api.dartlang.org/stable/1.24.3/dart-core/Uri/encodeQueryComponent.html
https://api.dartlang.org/stable/1.24.3/dart-core/Uri/encodeFull.html
https://api.dartlang.org/stable/1.24.3/dart-core/Uri/encodeComponent.html
You can use Uri.parse(url_string) if you have the full URL in this way.
final String accountEndPoint = 'https://api.npoint.io/2e4ef87d9ewqf01e481e';
Future<Account> getAccountData() async {
try {
final uri = Uri.parse(accountEndPoint); // <===
final response = await http.get(uri);
if (response.statusCode == 200) {
Map<String, dynamic> accountJson = jsonDecode(response.body);
return Future.value(Account.fromJson(accountJson));
} else {
throw Exception('Failed to get account');
}
} catch (e) {
return Future.error(e);
}
}
I am calling an api. I am getting a streamed response after sending the request. But i cannot parse the response and convert it to String/JSON. This is where I am calling the api.
static Future<String> callDeviceListFetchApi() async {
Completer completer = new Completer();
String jsonResponse;
String url = Constants.BASE_URL + Constants.DEVICE_REGISTER_URL;
var client = new http.Client();
var request = new http.Request('GET', Uri.parse(url));
request.headers[HttpHeaders.CONTENT_TYPE] = 'application/json';
request.headers[HttpHeaders.AUTHORIZATION] = '<auth code>';
await client.send(request).then((response) {
response.stream.bytesToString().then((value) {
print(value.toString());
jsonResponse = value.toString();
completer.complete(jsonResponse);
});
}).catchError((error) {
print(error.toString());
});
return completer.future;
}
I am getting the error,
Bad state: Stream has already been listened to Flutter error. Any idea why this is happening?
There's a couple of things wrong with your code. I think you have a slight misunderstanding about how Async and Futures work in dart - you should re-read the docs and this tutorial (part 1 and part 2).
Basically, the problem is that you were returning a 'Future' from an async function. If you return a future from an async function, it has issues (I don't know why the analyzer doesn't catch that).
Future<String> callDeviceListFetchApi() async {
Completer completer = new Completer();
String url = "<url>";
var client = new http.Client();
var request = new http.Request('GET', Uri.parse(url));
request.headers[HttpHeaders.CONTENT_TYPE] = 'application/json';
request.headers[HttpHeaders.AUTHORIZATION] =
'<auth string>';
var response = await client.send(request);
String jsonResponse;
try {
var value = await response.stream.bytesToString();
print(value.toString());
jsonResponse = value.toString();
} catch (error) {
print(error.toString());
}
return completer.complete(jsonResponse);
}
Or not async:
Future<String> callDeviceListFetchApiNotAsync() {
String url = "<url>";
var client = new http.Client();
var request = new http.Request('GET', Uri.parse(url));
request.headers[HttpHeaders.CONTENT_TYPE] = 'application/json';
request.headers[HttpHeaders.AUTHORIZATION] =
'<auth string>';
Completer completer = new Completer();
return client.send(request).then((response) {
return response.stream.bytesToString();
}).then((value) {
print(value.toString());
return value.toString();
}).catchError((error) {
print(error.toString());
// if you use catchError, whatever you return from it
// is the value you'll get wherever you resolve the future.
return null;
});
}
But unless you're trying to do something I'm not seeing, there's a way easier way to do this (assuming all you want to do is get a string from a server):
Future<String> getList() async {
var response = await http.get("<url>", headers: {
HttpHeaders.CONTENT_TYPE: 'application/json',
HttpHeaders.AUTHORIZATION: '<auth string>',
});
if (response.statusCode == 200) {
return response.body;
} else {
throw Error();
}
}
I have two classes, a user_api_manager and a base_api_manager. From user_api_manager i call the get method of base_api_manager which performs an http get request and returns a Future<String>. The getrequest is performed but i am not pass the result to my user_api_manager class. The callback result is always null.
This is my user_api_manager.dart
static Future<Map<String,dynamic>> forgotPasswordAPI(String email) async{
String url = Constants.BASE_URL + Constants.FORGOT_PASSWORD_URL + email;
await BaseApiManager.get(url: url).then((val) {
var response = JSON.decode(val);
var status = response['status'];
String message = '';
print(response);
switch (response['status']) {
case Constants.SUCCESS:
message = Constants.SUCCESS_RESPONSE;
break;
case Constants.SERVER_ERROR:
message = Constants.SERVER_ERROR_MESSAGE;
break;
case Constants.UNAUTHORISED:
message = Constants.UNAUTHORISED_MESSAGE;
break;
}
return {'status':status,'message':message};
});
}
and here is my base_api_manager.dart
static Future<String> get({url : String,
parameters : Map ,
headers: Map }) async {
var client = new http.Client();
Map<String,dynamic> resultJSON;
final c = new Completer();
await client.get(url).then((response) { //response is always null
resultJSON = {
'status' : response.statusCode,
'body' : JSON.decode(response.body)
};
c.complete(resultJSON.toString());
return c.future;
});
}
How to solve this issue?
Move the return c.future outside of the response processing, i.e you want to return this from your get otherwise you will return null.
You can simplify the code. That should make it easier to locate the problem
static Future<String> get({url : String, parameters : Map, headers: Map }) async {
var client = new http.Client();
final response = await client.get(url);
print(response.body);
var resultJSON = {
'status' : response.statusCode,
'body' : JSON.decode(response.body)
};
return resultJSON.toString()
}
What does that code print?