I've fetched a json object and deserialized it and then returned it too.
I want to use this in another file.
I'm unable to assign the values that I'm getting in the first step.
Here are all the codes...
Service
Future getGeoPoints(String accessToken, String tripId) async {
String requestUrl;
var response = await get(
Uri.parse(requestUrl),
headers: {
'Authorization': "Bearer $accessToken",
},
);
if (response.statusCode == 200) {
Map<String, dynamic> responseBody = json.decode(response.body);
GetGeoPoints geoPoints = GetGeoPoints.fromJson(responseBody);
List listOfGeoPoints = [];
for (var geoPoint in geoPoints.geoPoints) {
listOfGeoPoints.add(
{
'latitude': geoPoint.latitude,
'longitude': geoPoint.longitude,
'timestamp': geoPoint.timeStamp,
},
);
}
// print('List of geo points: ' + '$listOfGeoPoints');
return listOfGeoPoints;
} else {
throw Exception('Failed to load data from server');
}
}
File where I need the above values
List routeCoordinates;
Future<void> getValues() async {
getGeoPoints(widget.accessToken, widget.tripId)
.then((value) => routeCoordinates = value);
}
When I run the app, routeCoordinates is null but when I hotreload, it contains the value.
I want to have the values as soon as the screen starts. What is the right way to assign the values here?
I've also tried this:
routeCoordinates = getGeoPoints...
It throws error..
Please help.. Thanks..
The function getGeoPoints() is an asynchronous one. But on the other file, you are not using the await keyword, instead you are using then(). So your code is not waiting for that function to return value.
Try using below code,
List routeCoordinates;
Future<void> getValues() async {
routeCoordinates = await getGeoPoints(widget.accessToken, widget.tripId);
}
Let us know how it went.
You need to use a FutureBuilder to define a behaviour depending on the state of the request. You'll be able to tell the widget what to return while your app is waiting for the response to your request. You can also return a specific widget if you get an error(if your user is offline, for example).
Edit: I've linked the official docs but give this article a read if it's not clear enough.
Related
I am creating an ecommerce Android flutter application, and I am new to this dart language. I need to get data from one table and post it to another table, where the API is built in .NET Core using a SQL Server database.
This is my code:
httpService.getPosts().then((value) {
if (value != null) {
value.forEach((element) {
httpServices.addPosts(
0,
element.cartProductID, element.productBrandId,
element.cartUserID, element.item,
element.quantity, element.price,
element.totalPrice,
element.discount,
// element.isOrdered,
element.paymentID,
element.paymentMode,
element.date,
);
});
My get method
class GetOrderHttpService with ChangeNotifier {
Future<List<OrderTotal>> getPosts() async {
Response res =
await http.get(Uri.https('********'));
if (res.statusCode == 200) {
List<dynamic> body = jsonDecode(res.body);
List<OrderTotal> posts = body
.map(
(dynamic dynamic) => OrderTotal.fromJson(dynamic),
)
.toList();
notifyListeners();
return posts;
} else {
throw "Unable to retrieve posts.";
}
}
}
Future<bool> addPosts(
int orderID,
int orderProductID,
int productBrandId,
int orderUserID,
String item,
int quantity,
double price,
double totalPrice,
double discount,
int paymentID,
String? paymentMode,
DateTime date,
) async {
var response = await http.post(
Uri.https('************'),
body: jsonEncode({
'orderID': orderID,
'orderProductID': orderProductID,
'productBrandId': productBrandId,
'orderUserID': orderUserID,
'item': item,
'quantity': quantity,
'price': price,
'totalPrice': totalPrice,
'discount': discount,
'paymentID': paymentID,
'paymentMode': paymentMode,
'date': date
}),
headers: {
"Accept": "application/json",
"content-type": "application/json"
});
var data = response.body;
if (response.statusCode == 200) {
return true;
} else
throw Exception();
}
}
It successfully retrieves the data and passes it on to the future post method, but the database is not updated. When the breakpoint hits the post method, it doesn't go through the code and doesn't get any status code. Thank you
Notice you are using Future in both your get() and post() methods, but, when calling these methods you are not using the "await" keyword. You should use it every time you call a Future function assuring you are waiting that method to complete and retrieve data successfully. It might work without it (as you say your get method works) but, in more complex situations this might not be the case due to asynchronous nature of these type of functions.
Your code should look like this:
await httpService.getPosts().then((value) async {
if (value != null) {
value.forEach((element) {
await httpServices.addPosts(
0,
element.cartProductID, element.productBrandId,
element.cartUserID, element.item,
element.quantity, element.price,
element.totalPrice,
element.discount,
// element.isOrdered,
element.paymentID,
element.paymentMode,
element.date,
);
});
Hope this works. Have a nice day!
How do I set up a flutter method to return a future value that is drawn from the results of a future http post call inside the method?
The example code below is making a call to a web URL to add a new product. I want this method to return just the Id of the newly created product (i.e. 'name' inside response)
Future<String> add(Product aNewProduct) async {
var aUrl = Uri.parse(dbUrl);
http.post(aUrl,body: toBody(aNewProduct),).then((response) {
var aStr = json.decode(response.body)['name'];
return Future<String>.value(aStr);
});
}
With the code above, the parser is showing the following error/warning...
The body might complete normally, causing 'null' to be returned,
but the return type, 'FutureOr<String>', is a potentially non-nullable type.
(Documentation) Try adding either a return or a throw statement at the end.
Any suggestions on how to fix this?
You can use the await to get the value of a Future or rather your http request. After that you can simple decode it and return your desired behavior.
Future<String> add(Product aNewProduct) async {
var aUrl = Uri.parse(dbUrl);
final response = http.post(
aUrl,
body: toBody(aNewProduct),
);
return json.decode(response.body)['name'];
}
try this:
Future<String> add(Product aNewProduct) async {
var aUrl = Uri.parse(dbUrl);
var response= await http.post(aUrl,body: toBody(aNewProduct),);
if(response.statusCode==200){
var rawData = await response.stream.bytesToString();
Map data=json.decode(rawData);
return data['name'];
}else{
return '';
}
}
It is as simple as putting a return before the http.post statement
I have a method inside a service class:
#override
Future<String> registerNewVoter(Object deviceAppInfo) async {
Dio dio = new Dio();
final url = API().endpointVoterUri(EndpointVoter.newVoter).toString();
final header = {'Content-type': 'application/json'};
final data = await deviceAppInfo; ///need to call the method getInfo() on the Object class which returns a future
final response =
await dio.post(url, data: data, options: Options(headers: header));
if (response.statusCode == 200) {
Map map = response.data;
final uuid = map['result']['voter_uuid'];
return uuid;
}
print(
'Request $url failed\nResponse: ${response.statusCode} ${response.statusMessage}');
throw response;
}
I'm using type Object deviceAppInfo as a parameter in the method to keep the service as pure as possible(adhering to mvvm principles). The subclass is DeviceAppInfo which has an async method called getInfo()(and where the data comes from) which is supposed to be assigned to data(see the comments in the code). I'm struggling to see how I can keep the class decoupled from DeviceAppInfo class. Any suggestions...? I'm thinking of calling a factory constructor but not sure how to implement it. Here is my DeviceAppInfo class:
class DeviceAppInfo {
DeviceAppInfo({
this.platform,
this.platformVersion,
this.appVersion,
});
final String platform;
final String platformVersion;
final String appVersion;
Map<String, dynamic> toMap() => {
'platform': this.platform,
'platform_version': this.platformVersion,
'app_version': this.appVersion,
};
Future<Map<String, dynamic>> getInfo() async {
final values = await Future.wait([
getPlatform(),
getPlatformVersion(),
getProjectVersion(),
]);
return DeviceAppInfo(
platform: values[0],
platformVersion: values[1],
appVersion: values[2],
).toMap();
}
Future<String> getPlatform() async {
try {
if (Platform.isIOS) {
return 'ios';
}
return 'android';
} on PlatformException catch (e) {
return e.toString();
}
}
Future<String> getPlatformVersion() async {
try {
final platformVersion = await GetVersion.platformVersion;
return platformVersion;
} on PlatformException catch (e) {
return e.toString();
}
}
Future<String> getProjectVersion() async {
try {
final projectVersion = await GetVersion.projectVersion;
return projectVersion;
} on PlatformException catch (e) {
return e.toString();
}
}
}
I believe that DeviceAppInfo is a clear collaborator of your service, and hiding it behind Object is simply bad engineering:
it will make your Api hard to use correctly and easy to use incorrectly
Your api is no longer self-documenting, without reading the docs or code it is impossible to use it correctly.
However, it can be discussed if it should be exposed as a parameter or provided to the constructor of your service.
Having said that, There are at least 3 options that will decouple your service from DeviceAppInfo:
Option 1: Pass in the result of getInfo() to your method
least questionable and a common form of decoupling inbound data
I am a bit sceptical if you use a Map as an input type, it is still easy to provide a map with incorrect keys
Option 2: take a function as an argument
Function a bit harder to use, it is not evident what functions accross the codebase can be used (compared to a class)
Option 3: cast to dynamic
Please dont do that
Most closely matches your goal from question
function is extremely hard to use correctly Without reading docs / code
You change compile-time errors to runtime errors
Is this what you want?
#override
Future<String> registerNewVoter(DeviceAppInfo deviceAppInfo) async {
Dio dio = new Dio();
final url = API().endpointVoterUri(EndpointVoter.newVoter).toString();
final header = {'Content-type': 'application/json'};
final data = await deviceAppInfo.getInfo();
final response =
await dio.post(url, data: data, options: Options(headers: header));
if (response.statusCode == 200) {
Map map = response.data;
final uuid = map['result']['voter_uuid'];
return uuid;
}
print(
'Request $url failed\nResponse: ${response.statusCode} ${response.statusMessage}');
throw response;
}
NOTE: I just changed the type of deviceAppInfo from Object to DeviceAppInfo
i want to get the data returned from a method 'getData()' into a List 'datat' but i am getting this error
: ' The method '[]' was called on null '
this is the corresponding code :
ListeMedecinsState() {
/* Fetching Data Into ListView */
Future<String> getData() async {
var response = await http.get(
Uri.encodeFull("http://10.0.2.2:4000/user/GetAllMedecins"),
headers: {
"Accept": "application/json"
}
);
this.setState(() {
this.data = json.decode(response.body);
});
return "Success";
}
getData() ;
print(this.data[1]["email"]);
/* Fetching Data Into ListView */
}
Notice : when i retreive the data into the list inside the method it shows no error, i want to use the data outside of the method scope how can i do please ?
Also, you could do it this way:
getData().then((data){
//do something with data
});
You should await getData();
But please post your full code so it's more understandable.
Thank you
#Sebastian is correct. (so select his answer not mine. I'm only posting here so that you can see the complete code) Your code should read:
ListeMedecinsState() async { // <-- this has to be added as well
/* Fetching Data Into ListView */
Future<String> getData() async {
var response = await http.get(
Uri.encodeFull("http://10.0.2.2:4000/user/GetAllMedecins"),
headers: {
"Accept": "application/json"
}
);
this.setState(() {
this.data = json.decode(response.body);
});
return "Success";
}
await getData() ; // <--- your code needs to pause until the Future returns.
print(this.data[1]["email"]);
/* Fetching Data Into ListView */ }
As Sebastian said, your ListeMedecinState should be asynchronous.
So that you can await the request and only proceed on it completion.
Your code actually doesn't wait for the result of getData() method before proceeding.
So you should await it before trying to access the data fetched !
Hope this helps !
(Seems like you speak French, I speak French too just in case you get lost) :)
I have a link.txt file that i want to display it's content on a text widget
I tried this approach
try {
HttpClient client = HttpClient();
client.getUrl(Uri.parse(arbitaryLink.txt)).then((HttpClientRequest request) {
return request.close();
}).then((HttpClientResponse response) {
response
.transform(Utf8Decoder())
.listen((contents) {
// Do something here with contents
return Text(contents);
});
});
} catch (exception) {
print(exception);
}
contents is the response text .. whenever i try using it outside of it's scope, i get null value.
i tried returning Text, i even tried assigning it to a static variable but i got nothing.
Soo.. What am i doing wrong?
This is how i solved
Future<String> _fetchBtaqa() async {
final response = await http.get('https://www.albetaqa.site/social/data/alwaraqa/02quran/1quran03/p-quran116.txt');
if (response.statusCode == 200) {
var decoded = utf8.decode(response.bodyBytes);
print(decoded);
return decoded;
} else {
return "Error";
}
}
If by outside its scope you mean that you are returning the value of the response and then trying to use it in some widget, then the case might be that you are not waiting for the client to fetch the response.
1) make sure the when you print contents in the scope you are getting a value
2) put the api call or the networking call in a function and add the await keyword to wait for the response.
3) When the response succeeds you can call setState to rebuild the widget with the new value of contents