Why future is not getting complete? - dart

I need to get all the document inside this collection which much these query - I'm receiving all the documents successfully but the future never ends.
I tried WhenComplete but still not working.
Future<Null> getOldVac(anId) async {
print("getOldVac");
await Firestore.instance
.collection("users")
.document(userId)
.collection("animals")
.document(anId)
.collection("anMedication")
.where("type", isEqualTo: "vac")
.where("result", isEqualTo: "")
.snapshots()
.forEach((onValue) {
print(onValue);
}).then((onValue) {
print("Done");
}).catchError((onError) {
print(onError);
});
}
I need to print "Done" once all the future is complete.

You need to return something, can be the null that you are using or a void or throw some error.
try to put return in the front of your await like this:
return await Firestore.instance
.collection("users")
.document(userId)
.collection("animals")
.document(anId)
.collection("anMedication")
.where("type", isEqualTo: "vac")
.where("result", isEqualTo: "")
.snapshots()
.forEach((onValue) {
print(onValue);
return null;
}).then((onValue) {
print("Done");
return null;
}).catchError((onError) {
print(onError);
throw onError;
});

You are mixing promises with async await!

Related

Unhandled Exception: Bad state: cannot get a field on a DocumentSnapshotPlatform which does

I want to display the current user name on my app.
I tried this but got the error
Unhandled Exception: Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist
Future getUserData() async {
User? user = await FirebaseAuth.instance.currentUser;
final DocumentSnapshot doc = await FirebaseFirestore.instance
.collection("UserData")
.doc(user!.uid)
.get();
name = doc['name'];
print("name $name");
}
Then, I tried this:
Future getUserData() async {
User? user = await FirebaseAuth.instance.currentUser;
try {
final doc = await FirebaseFirestore.instance
.collection("UserData")
.doc(user!.uid)
.get();
final ds = await doc.get();
final data = ds.data() as Map<String, dynamic>;
name = data['name'];
print("name $name");
} catch (e) {
print(e.toString());
return null;
}
}
But it shows an error for the doc.get()
1 positional argument(s) expected, but 0 found.
Try adding the missing arguments
What can I do?
.get method expect a key('String') of filed/filePath.
final doc = await FirebaseFirestore.instance
.collection("UserData")
.doc(user!.uid)
.get();
final name = await doc.get("name");
print("name $name");
Also make sure the project setup is ok. More about reading data.

Flutter web: Values retrieved from Firestore map are truncated when added to List

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]

How to "await" non-future variable?

I have DocumentReference locationDocumentRef; in my state.
I'm changing locationDocumentRef based on the references, whether I gather by querying or by adding new document.
So I have this function to check the documents, if there is one set its reference to the locationDocumentRef, or add a new one and set its ref to the locationDocumentRef. I'm resetting its value everytime by setting it to null, since I didn't want to get previous result. But it prints null.
So my question is, how can I resolve them and get the value? I think I'm resolving too early in my code, so I can't await a non-future value. How can I fix it?
void firestoreCheckAndPush() async {
setState(() {
locationDocumentRef = null;
});
bool nameExists = await doesNameAlreadyExist(placeDetail.name);
if (nameExists) {
print('name exist');
} else {
print('name will be pushed on firestore');
pushNameToFirestore(placeDetail);
}
var resolvedRef = await locationDocumentRef;
print(resolvedRef.documentID); // I get null here
}
These are the functions that I have used
Future<bool> doesNameAlreadyExist(String name) async {
QuerySnapshot queryDb = await Firestore.instance
.collection('locations')
.where("city", isEqualTo: '${name}')
.limit(1)
.getDocuments();
if (queryDb.documents.length == 1) {
setState(() {
locationDocumentRef = queryDb.documents[0].reference;
});
return true;
} else {
return false;
}
}
And the other
void pushNameToFirestore(PlaceDetails pd) async {
DocumentReference justAddedRef =
await Firestore.instance.collection('locations').add(<String, String>{
'city': '${pd.name}',
'image': '${buildPhotoURL(pd.photos[0].photoReference)}',
});
setState(() {
locationDocumentRef = justAddedRef;
});
}
there is two mistakes i saw first here
var resolvedRef = await locationDocumentRef;
why you await for locationDocumentRef,
second you dont wait for pushNameToFirestore(PlaceDetails pd) firestoreCheckAndPush() function which is weird since pushNameToFirestore(String) is sync and this means you wouldnt wait for it to finish so if you are adding a new name it would print null.
correct me if i am wrong.
you can find more about sync and future here https://www.dartlang.org/tutorials/language/futures
look at the graph at the middle of the page
Try this
Future<List<DocumentSnapshot>> doesNameAlreadyExist(String name) async {
QuerySnapshot data = await Firestore.instance
.collection('locations')
.where("city", isEqualTo: name)
.limit(1)
.getDocuments();
return data.documents;
}
void firestoreCheckAndPush() async {
var data = await doesNameAlreadyExist('yourname');
if (data.length > 0) {
print('name exist');;
print('Document id '+ data[0].documentID);
} else {
print('name will be pushed on firestore');
}
}
Take a look into following code.
void firestoreCheckAndPush() async {
DocumentReference documentReference;
var data = await doesNameAlreadyExist('yourname');
var dataRef = await doesNameAlreadyExist('yourname');
if (data.length > 0) {
print('name exist');
documentReference = dataRef[0].reference;
print('Document id ' + data[0].documentID);
documentReference = dataRef[0].reference;
print('Document reference ');
print(documentReference);
} else {
print('name will be pushed on firestore');
}
}

How to return from then of a Future in dart

I have a function which does some asynchronous operations and I want to return the status of the operation back to the caller. How can I achieve this?
Future<bool> setData() async {
Firestore.instance.collection("test").document('$id').setData({
'id': 'test'
}).then((onValue) {
print('Data set success');
return true;
}).catchError((onError) {
print('Data set Error!!!');
return false;
});
}
//Calling
final status = await setData();
if(status){
//do success
}
But this function complains that it doesn't end with a return statement. What is the logical mistake I'm making here?
You miss a return in your setData function
return Firestore.instance....

Flutter/Dart Async Not Waiting

I'm building my first Flutter application and I've run into a bit of an async issue.
When my application executes I'd like it to ask for permissions and wait until they are granted. My main() function looks like this:
import 'permission_manager.dart' as Perm_Manager;
void main() async
{
//Ensure valid permissions
Perm_Manager.Permission_Manager pm = Perm_Manager.Permission_Manager();
var res = await pm.get_permissions();
print(res);
return runApp(MyApp());
}
The Permission Manager class' get_permissions() function uses the Flutter Simple Permissions package to check and ask for permissions.
import 'package:simple_permissions/simple_permissions.dart';
import 'dart:io' as IO;
import 'dart:async';
class Permission_Manager {
/* Get user permissions */
Future<bool> get_permissions() async
{
//Android handler
if (IO.Platform.isAndroid)
{
//Check for read permissions
SimplePermissions.checkPermission(Permission.ReadExternalStorage).then((result)
{
//If granted
if (result)
return true;
//Otherwise request them
else
{
SimplePermissions.requestPermission(Permission.ReadExternalStorage)
.then((result)
{
// Determine if they were granted
if (result == PermissionStatus.authorized)
return true;
else
IO.exit(0); //TODO - display a message
});
}
});
}
else
return true;
}
}
When I run the application it does not wait for the function to complete as intended and prints the value of "res" before the Future is updated.
Launching lib\main.dart on Android SDK built for x86 in debug mode...
Built build\app\outputs\apk\debug\app-debug.apk.
I/SimplePermission(15066): Checking permission : android.permission.READ_EXTERNAL_STORAGE
I/flutter (15066): null
I/SimplePermission(15066): Requesting permission : android.permission.READ_EXTERNAL_STORAGE
The Future returns a value midway through the function! Does anyone know what I'm doing wrong?
To await something you have to call the await keyword on a future instead of .then
final result = await future;
// do something
instead of
future.then((result) {
// do something
});
If you really want to use .then then you can await the generated future:
await future.then((result) {
// do something
});
Just ensure that when using nested asynchronous calls that the async keyword is used on each:
await future.then((result) async{
// do something
await future.then((result_2) {
// do something else
});
});
Got it working. The issue seems to be resolved using a Completer:
import 'package:simple_permissions/simple_permissions.dart';
import 'dart:io' as IO;
import 'dart:async';
class Permission_Manager {
/* Get user permissions */
final Completer c = new Completer();
Future get_permissions() async
{
//Android handler
if (IO.Platform.isAndroid)
{
//Check for read permissions
SimplePermissions.checkPermission(Permission.ReadExternalStorage).then((result)
{
//If granted
if (result)
{
c.complete(true);
}
//Otherwise request them
else
{
SimplePermissions.requestPermission(Permission.ReadExternalStorage).then((result)
{
// Determine if they were granted
if (result == PermissionStatus.authorized)
{
c.complete(true);
}
else
{
IO.exit(0); //TODO - display a message
}
});
}
});
}
else
{
c.complete(true);
}
return c.future;
}
}

Resources