Why am I getting 'Future<dynamic>' instead of the return value in the function? - flutter

I'm trying to get the return value in my function but the output is 'Instance of Future' instead of the value of school field name in the database
#override
void initState() {
userId = _auth.currentUser!.uid;
publisherSchool =
getName(widget.postInfo['publisher-Id'], 'school').toString();
super.initState();
}
Future getName(String publisherUid, String fieldname) async {
DocumentSnapshot publisherSnapshot = await FirebaseFirestore.instance
.collection('users')
.doc(publisherUid)
.get();
print(publisherSnapshot.get(fieldname));
return publisherSnapshot.get(fieldname);
}
but whenever i'm printing the publisherSnapshop.get(fieldname) i'm getting the correct value from the database

There are 2 ways to do it, you can create a Future method and call it inside the initState like below:
#override
void initState() {
initial();
super.initState();
}
Future<void> initial() async {
userId = _auth.currentUser!.uid;
// Remember using `()` to wrap the `await` to get it result
publisherSchool = (await getName(widget.postInfo['publisher-Id'], 'school')).toString();
}
Or you can use .then to call it directly inside the initState:
#override
void initState() {
userId = _auth.currentUser!.uid;
getName(widget.postInfo['publisher-Id'], 'school').then((value) {
publisherSchool = value.toString();
});
super.initState();
}

When you declare the getName() function, specify the return type as Future<String>, and then when you call getName(), you need to await the result e.g. publisherSchool = await getName(widget.postInfo['publisher-Id'], 'school').toString();

The reason why you are not getting the correct response is because whenever you are working with Futures it takes some time to finish and return the results. Meanwhile it is fetching the result you have to make it await so that the program will continue once that future function is complete since await/then is nowhere to be found in your code hence the issues.
To solve this make this change:
Change
publisherSchool =
getName(widget.postInfo['publisher-Id'], 'school').toString();
To
getName(widget.postInfo['publisher-Id'],
'school').then((value){
publisherSchool=value.toString()});

Related

Flutter ensure I have a value in Async/Await and init functions [duplicate]

This question already has answers here:
What is a Future and how do I use it?
(6 answers)
Closed 20 days ago.
How can I make sure I have a state variable available after an async function call? My belief is because getValues() is async, it should "wait" until moving on to the next line. Thus, getValues() shouldn't exit and configValue() shouldn't be invoked until after my call to setState has finished. However the behavior I'm seeing it that values is an empty array in my Widget.
late List values = [];
#override
void initState() {
super.initState();
getValues();
configValue();
}
getValues() async {
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
setState(() {
values = vals;
});
}
void configValue() {
// How to make sure I have values[0] here?
}
Thanks in advance!
You can change your getValues to this:
Future<List> getValues() async {
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
return vals;
}
then create another middle function like this:
callasyncs() async {
var result = await getValues();
configValue(result);
}
and call it inside initState like this:
#override
void initState() {
super.initState();
callasyncs();
}
also change your configValue to this:
void configValue(List values) {
// now you have updated values here.
}
here your both configValue and getValues are separated from each other and also your configValue will wait for the getValues result.
you need to use await before the method to complete the future. also can be use .then.
Future<void> getVids() async { //I prefer retuning value
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
setState(() {
values = vals;
});
}
void configValue() async {
await getVids();
}
Try the following code:
List? values;
#override
void initState() {
super.initState();
getValues();
configValue();
}
Future<void> getVids() async {
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
setState(() {
values = vals;
});
}
void configValue() {
if (values != null) {
if (values!.isNotEmpty) {
…
}
}
}

Instance of _Future<int> is all I get when I try to get the total 'document' of my 'collection' in Firestore

Following is my code. I'm trying to get all the 'Babies' which are in documents:
class _HomePageeState extends State<HomePagee> {
String t_babies = getCount().toString();
}
Future getCount() async {
return FirebaseFirestore.instance.collection('Babies').snapshots().length;
}
Instead I get this error: instance of \_future\<int\>
Here is my Database. I expect to get 2 counts:
You need to use await when getting Future values and also you should pass Future and the type Future<int>:
Future<int> getCount() async {
return await FirebaseFirestore.instance.collection('Babies').snapshots().length;
}
and also get the method using await but inside and async function:
void example() async { // <---- here the async you need to add to use await
int babiesLength = await getCount(); // here use await
}
You should use setState to update the string , because the fetch takes time as it involves network.
String t_babies = '';
Future<void> _getCount() async {
setState((){
t_babies = FirebaseFirestore.instance.collection('Babies').snapshots().length.toString();
});
}
#override
void initState() {
super.initState();
_getCount();
}

Future<dynamic> is not a subtype of List<dynamic>

So I am trying to pass a list of String values from firestore table, but I am getting an exception type 'Future<dynamic>' is not a subtype of type 'List<dynamic>'
This is the function
getLectureList(String userId) async {
var collection = FirebaseFirestore.instance.collection('students');
var docSnapshot = await collection.doc(userId).get();
Map<String, dynamic>? data = docSnapshot.data();
List<String> _lectureList =
await data!['attendance']; //This line is kinda giving me trouble
userInfo = FirestoreWrapper()
.getStudentFromData(docId: currentUser(), rawData: data);
return _lectureList;
}
And this is the function where I am getting the exception thrown
#override
void initState() {
lectureList = getLectureList(currentUser()); // Getting an exception here
NearbyConn(context).searchDevices(devices: deviceList);
super.initState();
}
tried using await in the getLectureList() method but still getting the same problem
Why do you await your data? You already got it.
List<String> _lectureList = data!['attendance'];
Please note that I don't know what your data structure looks like, so I cannot tell you if this is correct, I can only tell you that it is more correct than before, because the await did not belong there.
You are getting an exception here lectureList = getLectureList(currentUser()); because the the parameter required by the getLectureList() method is the userId which is a string. I do not know what currentUser() return but I'm assuming it's the userId that you need when calling the getLectureList() method. Based on the error, it looks like currentUser() is an async method that returns a future after some time.
You're not awaiting that future. You shouldn't make the initState() method async so move the code block out of it into a separate method and then call it from initState().
Something like this,
#override
void initState() {
super.initState();
_getData();
}
void _getData() async {
lectureList =
getLectureList(await currentUser());
NearbyConn(context).searchDevices(devices: deviceList);
}
or
#override
void initState() {
super.initState();
_getData();
}
void _getData() async {
String _userID = await currentUser();
lectureList = getLectureList(_userID);
NearbyConn(context).searchDevices(devices: deviceList);
}
Which I recommend so you can see all the parts.
Making your method parameters required named parameters also help you to easily see what is needed to pass to a function/class/.
Eg.
getLectureList({required String userId}){
...
}
Your IDE will alert you on the type of object the function requires and it makes things clearer.
Ultimately, I think typing your classes makes it so much more easier to fetch data from fireStore Typing CollectionReference and DocumentReference
This way you can easily do this,
final moviesRef = FirebaseFirestore.instance.collection('movies').withConverter<Movie>(
fromFirestore: (snapshot, _) => Movie.fromJson(snapshot.data()!),
toFirestore: (movie, _) => movie.toJson(),
);
and get your data this way,
Future<void> main() async {
// Obtain science-fiction movies
List<QueryDocumentSnapshot<Movie>> movies = await moviesRef
.where('genre', isEqualTo: 'Sci-fi')
.get()
.then((snapshot) => snapshot.docs);
// Add a movie
await moviesRef.add(
Movie(
title: 'Star Wars: A New Hope (Episode IV)',
genre: 'Sci-fi'
),
);
// Get a movie with the id 42
Movie movie42 = await moviesRef.doc('42').get().then((snapshot) => snapshot.data()!);
}
Keeps everything dry and tidy.
< The data comes to list format thats why showing the exception of datatype >
List<String> lectureList = await getLectureList(currentUser()); // use
Future<List<String>> getLectureList(String userId) async {
- your code -
}
Instead of
List _lectureList =
await data!['attendance'];
Try this
_lectureList = await data![] As List

How to init state of widget variable with Future string in flutter

I have to initialise the state of widget variable with the value stored in StoredProcedure.
void initState() {
widget.query = fetchMake();
super.initState();
}
Future<String> fetchMake() async{
final prefs = await SharedPreferences.getInstance();
return prefs.getString(key);
query.toString();
}
But the issue is that it cant assign that value to query variable and showing error value to type future string cannot assign to string flutter
How can I do that?
fetchMake is async so you have to put await where you call that method but it will not work because you are calling it in initState.
So You have to assign widget.query variable value in that function only. Moreover, as you get data you have to call setState, so data you receive reflect in ui.
In addition to that you have to check query is null or not where you are using it because when first time build method call it will not have any data.
String query;
void initState() {
fetchMake();
super.initState();
}
fetchMake() async{
final prefs = await SharedPreferences.getInstance();
setState((){
query = prefs.getString(key) ?? 'default';
});
}
You can try:
String query;
void initState() {
super.initState();
fetchMake().then((value) {
setState(() {
query = value;
});
});
}
Future<String> fetchMake() async{
final prefs = await SharedPreferences.getInstance();
return prefs.getString(key);
}
1, You can not set state with widget.query.
2, Create a variable query on state of Widget.
3, fetchMake is a async function => using then to wait result.

Returning a single Firestore field value from a known documentID

I am wanting to do a simple lookup in Firestore to get a 'propertyName' for a given 'propertyID' (the 'properties' documentID) from the collection 'properties' and assign this to the variable propertyName.
The print() gives 'from initstate propertyName = Instance of 'Future' and not the actual value in Firestore. How do I extract the actual value?
I have tried also using StreamBuilder but keep having the same issue.
#override
void initState() {
super.initState();
var propertyName = _getPropertyNameFromPropertyID(
widget.propertyID); // to get propertyName from Firestore
print('from initstate propertyName = $propertyName');
}
Future _getPropertyNameFromPropertyID(propertyID) async {
DocumentSnapshot snapshot = await Firestore.instance
.collection('properties')
.document(propertyID)
.get();
String result = snapshot['propertyName'].toString();
return result;
}
That's because your method returns a Future so you will need to use async/await or just get the result directly from the Future.
Option 1
#override
void initState() {
super.initState();
_getPropertyNameFromPropertyID(widget.propertyID).then ((propertyName){
print('from initstate propertyName = $propertyName');
});
}
Option 2
#override
void initState() {
super.initState();
_loadAsyncData();
}
_loadAsyncData() async {
var propertyName = await _getPropertyNameFromPropertyID(
widget.propertyID); // to get propertyName from Firestore
print('from initstate propertyName = $propertyName');
}
Future _getPropertyNameFromPropertyID(propertyID) async {
DocumentSnapshot snapshot = await Firestore.instance
.collection('properties')
.document(propertyID)
.get();
String result = snapshot['propertyName'].toString();
return result;
}