Help is much appreciated how to trace down this issue, because I am running out of ideas.
I am calling the function getOrderCollection, below, but it aborts after the first line var myCompanyDoc = await FirebaseFirestore.instance.collection('companies').doc(myCompany).get(); Without trowing anything to the console or jumping into some library when debugging. When I click next statement it jumps back to the calling function.
I am authenticated to the database, companyCollection = FirebaseFirestore.instance.collection('companies') provides an initialized object pointing to the collection and myCompany is a constant with the document id entered by copy/paste.
If some rules for the database but I can't see successful or denied queries with the monitor.
Any ideas how I can proceed tracing down the issue?
Future<void> getOrderCollection() async {
var myCompanyDoc = await FirebaseFirestore.instance.collection('companies').doc(myCompany).get();
print("companyDoc fetched");
final myDeliveryDocRef = myCompanyDoc.data()['delivery'].toString();
orderCollection = FirebaseFirestore.instance.collection('companies').doc(myCompany).collection('features').doc(myDeliveryDocRef).collection('orders');
orderBriefDoc = FirebaseFirestore.instance.collection('companies').doc(myCompany).collection('features').doc(myDeliveryDocRef);
}
UPDATE: This is collection > document what corresponds to final String myCompany = '4U4kZKXkr3rHA6B04S5K';
As we discussed in your comments, the issue was that you forgot to await the getOrderCollection() function. Even though, as you mentioned, your caller function _deliveryRepository.initRepository() was awaited, you still had to await getOrderCollection() inside your caller method to make sure that the code is waiting for the getOrderCollection() to be executed before it proceeds to the next line.
In general, you want to have some error handling and to type the known types/classes (avoid using var).
Error handling - for async/await place the code inside a try/catch.
Typing - Dart is type safe, which is really great to prevent runtime errors.
Depending on your setup, you might be able to hover over the Firestore.instance.collection(...).doc(...) to see the return type. .doc(...).get() returns a DocumentSnapshot and .collection(...).get() returns a CollectionSnapshot.
Using the above, it should be easier to debug:
Future<void> getOrderCollection() async {
try {
DocumentSnapshot myCompanyDoc = await FirebaseFirestore.instance.collection('companies').doc(myCompany).get();
print("companyDoc fetched");
final myDeliveryDocRef = myCompanyDoc.data()['delivery'].toString();
} catch(e) {
print('Error: ' + e.toString());
}
}
Don't forget to await your other 2 Firestore queries.
Related
I am using flutter with the cbl package to persist data. Trying to retrieve the entries does not seem to work because the function created is returning at the await statement and not the return statement. This does not seem like the intended result of darts async/await functionality. So I am lost.
task_database.dart
Future<dynamic> getAllTasks() async {
final tasksDb = await Database.openAsync(database); <---------- Returns here
var tasksQuery = const QueryBuilder()
.select(SelectResult.all())
.from(DataSource.database(tasksDb));
final resultSet = await tasksQuery.execute();
late var task;
await for (final result in resultSet.asStream()) {
final map = result.toPlainMap();
final taskDao = TaskDao.fromJson(map);
task = taskDao.task;
// Do something with the task...
print(task);
}
;
return task; <-------------------------------------------- Does not make it here
}
task_cubit.dart
getAllTasks() => {
allTaskMap = TasksAbcDatabase().getAllTasks(),
emit(TaskState(tasks: state. Tasks))
};
What I have tried. I have tried to use Database.openSync instead of Database.openAsync however, the function just returns at the next await statement. I have also tried making getAllTasks asynchronous and awaiting the database as such.
Future<void> getAllTasks() async => {
allTaskMap = await TasksAbcDatabase().getAllTasks(),
emit(TaskState(tasks: state. Tasks))
};
However this has the same issue, when the function from task_database returns prematurely it the returns at the first await function in getAllTasks which is the allTaskMap variable.
Thanks
A function cannot "return prematurely" without a return statement.
The only way the execution is cut short would be an exception being thrown.
I also don't see how you don't get syntax errors, when you don't await the Database.openAsync(database) statement.
So make sure all your awaits are in place. Use the linter to find those that are missing. While you are at it, remove the keyword dynamic from your vocabulary, it will only hurt you if you use it without a need for it. Your return type should be properly typed, then your compiler could tell you, that returning a single task from a function that is clearly supposed to return multiple tasks is not going to work.
Either catch your exceptions and make sure you know there was one, or do not catch them and watch them go all the way through into your debugger.
In addition, following the comment of #jamesdlin, your function definitions are... valid, but probably not doing what you think they are doing.
Future<void> getAllTasks() async => {
allTaskMap = await TasksAbcDatabase().getAllTasks(),
emit(TaskState(tasks: state. Tasks))
};
needs to be
Future<void> getAllTasks() async {
allTaskMap = await TasksAbcDatabase().getAllTasks();
emit(TaskState(tasks: state. Tasks));
}
So, Supabase announced that they're updated supabase-flutter package into v.1.0 and I want to give it a try.
This is how they're fetching data from database in previous version:
final res = await supabase.from('my_table').select().execute(); // <= They're using execute
final error = res.error;
if(error != null) {
// handle error
}
final data = res.data;
But in this new version, they're deprecating .execute().
As the blog said,
No more .execute() to get the data
We want this SDK to be as close as possible to the JavaScript SDK to provide consistent developer experience no matter what programming language you are using. Prior to the 1.0 update, whenever you called the postgrest endpoints, you had to call .execute() at the end of each query.
.execute() is now deprecated. You no longer needed it to query data from your Supabase database.
This is the new syntax:
try {
final data = supabase.from('my_table').select();
// 'data' data type is PostgrestFilterBuilder
} catch (error) {
// handle error
}
How can I get value from variable 'data'? Also I want to get a single value from the data.
Any help would be appreciated
I think the issue is that you are missing a await before the supabase.from in your code.
I am trying to get documents with their own subcollections, from Stream, but I am stuck.
This is where I set up my StreamSubscription:
Future<void> _toggleOrdersHistorySubscription({FirebaseUser user}) async {
_ordersHistorySubscription?.cancel();
if (user != null) {
_ordersHistorySubscription = ordersRepository
.ordersHistoryStream(userId: user.uid)
.listen((ordersSnapshot) {
final List<OrderModel> tmpList = ordersSnapshot.documents.map((e) {
// e.reference.collection("cart").getDocuments().;
return OrderModel.orderFromSnapshot(e);
}).toList();
add(OrdersHistoryUpdated(ordersHistory: tmpList));
});
}
}
The issue is that I can't see a way to get subcollection along with the parent document because getDocuments returns a Future.
Anyone can clear this issue for me?
So, I update the code method a separate method for retrieving data when listener is triggered but it doesn't work fully and I do not understand what's happening and why part of the code is working and part is not.
List<OrderModel> _getOrdersHistory({
#required QuerySnapshot snapshot,
}) {
return snapshot.documents.map((document) {
List<OrderedProductModel> cart = [];
List<AddressModel> addresses = [];
document.reference.collection("cart").getDocuments().then((snapshot) {
snapshot?.documents?.forEach((doc) {
cart.add(OrderedProductModel.fromSnapshot(doc));
});
});
document.reference
.collection("addresses")
.getDocuments()
.then((snapshot) {
snapshot?.documents?.forEach((doc) {
addresses.add(AddressModel.addressFromJson(doc.data));
});
});
final order = OrderModel.orderFromSnapshot(
document,
restaurantCart: cart,
);
return order.copyWith(
orderAddress:
(addresses?.isNotEmpty ?? false) ? addresses.first : null,
sentFromAddress:
(addresses?.isNotEmpty ?? false) ? addresses.last : null,
);
})
.toList() ??
[];
}
As an alternate solution to my original issue is that I made a map entry in Firestore instead of a collection for 2 address documents (which are set above as orderAddress and sentFromAddress) and for the cart I decided to get the data when needed for every cart item.
So the method which I put in the update is not the final one, but I want to understand what is happening up there as I do not understand why:
Why the cart is shown as empty if I do a print(order); right before the return and in the bloc it has data;
Why the orderAddress and sentFromAddress are both empty no matter what I try;
To be short: You'll never be able to get a List synchronously if you get the data async from firebase.
Both questions have the same answer:
Your timeline:
For each document
Create an empty list
Initiate the firebase query - getDocuments()
Subscribe to the returned future with - .then((snapshot){cart.add(...)}).
This lambda will be invoked when the documents arrived.
Another subscribe
Save your empty cart and first/last of empty addresses to an OrderModel
Your List contains the references to your empty lists indirectly
Use your bloc, some time elapsed
Firebase done, your callbacks starts to fill up your lists.
Regarding your comment like stream.listen doesn't like async callbacks:
That's not true, you just have to know how async functions work. They're run synchronously until the first await, then return with an incomplete future. If you do real async things you have to deal with the consequences of the time delay like changed environment or parallel running listeners.
You can deal with parallel running with await for (T v in stream) or you can use subscription.pause() and resume.
If anything returns a future, just do this:
...getDocuments().then((value) => {
value is the item return here. Do something with it....
})
Also, you might want to split your method up a bit to share the responsibility.
If getDocuments is a Future function and you need to wait for it, I think you should add await before it. I also don't see the snapshot status checking in your code pasted here. Maybe you have already checked the snapshot status in other function? Make sure the snapshot is ready when using it.
I'm having some problems with a line in the function below. The function is handling async gets from Firebase Storage. I'm using it to get the names and urls of files I have stored there.
The issues is with getting the Urls. Specifically on the line:
String url = element.getDownloadURL().toString();
getDownloadedURL() is a Firebase future. I tried to await it, but it won't recognise the await, I guess due to "element".
The over all effect is that when I'm using this in my UI via a Future builder, the name comes out fine but the Url doesn't. It is being retrieved as the print statement shows it. But it's not being waited for, so the UI is already updated.
Been trying lots of things, but haven't found a solution, so any help would be greatly appreciated.
Future<void> getImageData() async {
final imagesFromStorage = await fb
.storage()
.refFromURL('gs://little-big-deals.appspot.com')
.child('images')
.listAll();
imagesFromStorage.items.forEach((element) {
print(element.name);
String url = element.getDownloadURL().toString();
print(url.toString());
imageData.add(ImageData(element.name, url.toString()));
});
}
Many thanks
You can't use async in forEach.
Just use a for loop:
Future<void> getImageData() async {
final imagesFromStorage = await fb
.storage()
.refFromURL('gs://little-big-deals.appspot.com')
.child('images')
.listAll();
for (var element in imagesFromStorage.items) {
print(element.name);
String url = (await element.getDownloadURL()).toString();
print(url.toString());
imageData.add(ImageData(element.name, url.toString()));
}
}
Please upvote: https://github.com/dart-lang/sdk/issues/40198
I am wondering how I can get access to the value of a future, while debugging in vs-code? Right now i need to introduce another temp variable to get the debugger to await the statement. This is how I am accessing a value at the moment:
Future<void> disconnect2() async {
var conn = await isConnected;
if (conn) await internalDisconnect();
}
But I would like to write the following:
Future<void> disconnect() async {
if (await isConnected) await internalDisconnect();
}
And still be able to access the value of isConnected with the debugger.
What I tried so far:
Try to add the expression (await isConnected) to the watch.
Tried to evaluate the expression (await isConnected) in the debug console.
Try to add the expression isConnected to the watch.
Tried to evaluate the expression isConnected in the debug console.
Tried to access the value through hovering over the instance. In the screenshot it is null, but most of the time I get to access only the future object.
Is there any easy way to access the value of a future while debugging?
You cannot see the value of future value while it is not awaited and loaded. The reason for it, future type is waiting the data, so it reserve part of memory for future value. You can access it only after getting the value f.e. value from REST API.
You can use then method and see the value there.
someFuture(arg).then((erg) => print(erg));