Implementing async / await in Dart/Flutter - flutter

Why the build method is showing 'NULL' at Restart, but update the Latitude value on hot-reload?
Is it possible to load the Text('Lat: $latitude') as initState() itself?
class _LoadingScreenState extends State<LoadingScreen> {
  double latitude;
  double longitude;
  #override
  void initState() {
    super.initState();
    getLocation();
  }
  void getLocation() async {
    Location location = Location();
    await location.getCurrentLocation();
    latitude = location.latitude;
    longitude = location.longitude;
  }
  #override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(child: Text('Lat: $latitude')),
    );
  }
}

That's because you haven't called the setState method when you changed your data, so the widget did not rebuild itself.
It should be like this:
void getLocation() async {
Location location = Location();
await location.getCurrentLocation();
setState(() {
latitude = location.latitude;
longitude = location.longitude;
});
}

latitude havn't had time to be assigned with a value when the build method is constructing the widget.
Wrap the assignment of latitude and longitude with the setState method to notify the framework that a new build should take place. That way the latitude value will be updated as soon as it is available.
setState(() {
latitude = location.latitude;
longitude = location.longitude;
});
A tip is to display something else instead of the latitude value while waiting for it to be assigned. This could e.g. be a CircularProgressIndicator.

as with the answer above, the getLocation is async meaning it will finish in future, on restart you get the current latitude which is null then as it didn't get the value yet, when hot reloading you get to show the value then which is already finished,
you can use setState in stateful class as above,
or you can use futurebuilder to show the value when available.

Related

setState() called in constructor: HomepageState#5ee97(lifecycle state: created, no widget, not mounted)

I have written this code here:
ElevatedButton(
onPressed:() async {
final HomepageState hs = new HomepageState();
Position position = await hs.getGeoLocationPosition();
String Address='';
print("position:");
print(position);
hs.GetAddressFromLatLong(position).then((String result) {
print("result:");
print(result);
setState(() {
if (result is String)
Address = result.toString();
});
});
launch('sms:+201100840233?body='+Address);
},
child:Text('Send Location'))
Which is supposed to use a method in the HomepageState of another .dart class to get a position object, pass it to another method in that class to obtain an Address string which will then be added to an SMS and sent. However, the text actually added to the SMS is always the initialized text "null". I suspect this has something to do with the error in the question title as it started appearing only after I wrote the hs.GetAddressFromLatLong(position) method. I have no idea how to solve this though. Can anyone please help me?
Maybe change your call to GetAddress to
address = await ha.getAdress…
Avoid uppercase for variables.
Also No need for result variable or setState()

How do I access variable from a function and use it in my widget - Flutter storage variable

I have this project, so I already stored the data I need in flutter storage but I'm unable to pick that data and use it in my text widget
checkname() async {
String? name = await storage.read(key: "name");
setState(() {
splitname = name;
});
print(splitname);
}
checkname();
I created this function to read the value in the storage but but it kept on printing the value about a 1000 times a minutes and I feel something might go wrong, is there a way I could go around it.
Printing the value about a 1000 times indicates that you are calling the function inside build method.
Here's on overall of what you should do:
create a statuful widget
call checkname inside initState function of the widget

Retrieve Data from Firestore and Pass it to a variable. Flutter

Hi i would like to ask how can I retrieve the data from the Firestore and send it to a double.
This is the code where I retrieve the data from Firestore.
Firestore.instance
.collection('locations')
.snapshots()
.listen((driverLocation){
driverLocation.documents.forEach((dLocation){
dLocation.data['Latitude'] = latitude;
dLocation.data['Longitude'] = longitude;
print(latitude);
});
});
I store it inside the dLocation and when i print(dLocation.data) it will display the latitude and longitude in the Firestore. But when i pass it to the double latitude and double longitude it returns null.
busStop.add(
Marker(
markerId: MarkerId('driverLocation')
,
draggable: false,
icon: BitmapDescriptor.defaultMarker,
onTap: () {
},
position: LatLng(latitude, longitude),
));
Then i would like to pass the data that is in the double latitude and double longitude into the marker so that the marker will move accordingly to the latitude and longitude in the Firestore.
Everything that is happening here is in a initState().
**If theres anything you would want to ask please feel free to do so as i do not have any idea on how to convey my question. Thank you so much in advance.
You're doing it in the wrong way. Right now you are assigning the value of latitude (which is null) to the value of dLocation.data['latitude']. What you want to do is this:
latitude = dLocation.data['latitude'];
longitude = dLocation.data['longitude'];
with this change, the value of dLocation.data['latitude'] will be assigned to latitude and the value of dLocation.data['longitude'] will be assigned to longitude variable
Update:
To get new markers and show them on the screen with latitude and longitude values, you can do something like this:
#override
void initState(){
Firestore.instance
.collection('locations')
.snapshots()
.listen((driverLocation){
//busStop = []; removes all the old markers and you don't get duplicate markers with different coordinates
driverLocation.documents.forEach((dLocation){
dLocation.data['Latitude'] = latitude;
dLocation.data['Longitude'] = longitude;
busStop.add(
Marker(
markerId: MarkerId('driverLocation')
,
draggable: false,
icon: BitmapDescriptor.defaultMarker,
onTap: () {
},
position: LatLng(latitude, longitude),
)); //you need to check for duplicate markers here. you can do it by giving each marker an unique id and check for the same marker in the list so you don't get duplicate markers.
});
setState((){}); //rebuilds the widget tree after adding the markers to the busStop list
});
}
What's happening here is you add the markers to the busStop list and after adding all the markers, you call setState and the widget tree rebuilds the screen with the latest data. You might need to check for duplicate markers because they might be re-added to the busStop list. Or you can simply remove all the old markers and add the new ones by using busStop = []; before adding to busStop

HTTP call on screen load in flutter

We have a Features class that we are trying to fill when a screen loads. Its an http call that returns the object. Im struggling with how to do this. All of our http calls are done on a button click:
here is the call
Future<Features> getFeatureStatus(String userID) async {
Features _features;
final response =
await http.post('http://url/api/Features',
headers: {"Content-Type": "application/json",
'Accept': 'application/json',},
body: json.encode({'userID' : userID }));
_features = Features.fromJson(json.decode(response.body));
return _features;
}
When i try to call it at the top of the class I get errors and cant get to the values.
class FlutterReduxApp extends StatelessWidget {
static final User user;
static final Features features = getFeatureStatus(user.userId);
The error I get is -- "A value of type 'Future' can't be assigned to a variable of type 'Features'.
Try changing the type of the variable, or casting the right-hand type to 'Features'.dart(invalid_assignment)"
Im sure im doing something incorrect here but I havent done a screen load call yet.
The getFeatureStatus function is returning a Future<Features> while you're trying to read it as type Features in the stateless widget.
There are different ways to read the value but since you have a button, you could convert the widget into a StatefulWidget then use the onPressed function to read the value and update the state afterwards such as.
onPressed: () async {
features = await getFeatureStatus(user.userId);
setState((){
// do something
});
}
In this case, the value features cannot be a static final so you'll have to change it to Features features.
Edit based on comment:
You could also do this inside an initState:
Features features;
#override
void initState () {
super.initState();
_asyncMethod();
}
_asyncMethod() async {
features = await getFeatureStatus(user.userId);
setState((){});
}
so in the widget build method you could do:
return (features == null)
? CircularProgressIndicator()
: MyWidget(...); // where features is used.
getFeatureStatus(user.userId).than((features)
{
// you will get the features object
//you can work on that object
}
);
calling the getFeaturesStatus method in the initState() when using the statefull.
First thing first, this line static final Features features = getFeatureStatus(user.userId); will not work as you are trying to assign a type Future to the type Features.
The solution for this is to await the future so that it resolves and returns a Feature data type that satisfies your variable named 'features'.
This goes as follows: static final Features features = await getFeatureStatus(user.userId); but this has to be in a separate function which is explicitly defined with the async parameter.
This solves the error in the respect of code that you have written, but as you stated that you want this to load after the screen loads (Or technically, when the main widget is "mounted").
The solution for this logical aspect can be the use of this.mounted.
All widgets have a bool this.mounted property. It turns true when the buildContext is assigned.
In short, suppose you want to run a function after any widget is mounted/loaded, you can test it via
if(this.mounted){
//Whatever you want to do when the widget has been mounted...
}

Empty set state what is the point?

I want to know the point behind calling setState without setting a new value to the variables.
readLocal() async {
prefs = await SharedPreferences.getInstance();
id = prefs.getString('id') ?? '';
if (id.hashCode <= peerId.hashCode) {
groupChatId = '$id-$peerId';
} else {
groupChatId = '$peerId-$id';
}
setState(() {});
}
I would say it's just a convention. The above can be re-written as
readLocal() async {
prefs = await SharedPreferences.getInstance();
setState(() {
id = prefs.getString('id') ?? '';
if (id.hashCode <= peerId.hashCode) {
groupChatId = '$id-$peerId';
} else {
groupChatId = '$peerId-$id';
}
});
}
Both will do the same thing. Calling setState(() {}) after mutating the state variable looks neat and reabable.
As per the implementation section of setState, it will below things in order.
Assertions. If any assert fails, throws exception and stops there.
Execute the callback function (final dynamic result = fn() as dynamic;)
Ask framework to rebuild(_element.markNeedsBuild();)
The documentation says [ https://docs.flutter.io/flutter/widgets/State/setState.html ]:
Calling setState notifies the framework that the internal state of this object has changed in a way that might impact the user interface in this subtree, which causes the framework to schedule a build for this State object.
The empty bracket { } is the empty callback (because you apparently don't need one):
The provided callback is immediately called synchronously. [...]
In short:
setState(() {});
is a way to tell the framework to build the state object anew, without using the possibility to pass a callback which would be called right after the build
Adding to the other answers, there is one difference.
When setState() is called on an unmounted (mounted == false) widget, it will fail. This means that whatever is wrapped inside the setState callback won't be called, whereas if you would run it outside setState it would be executed.