Dart non-async function return wait until for loop is complete - flutter

Here is my code
List<DocumentReference> getStacks(
List<DocumentReference> favoritePlayers,
List<DocumentReference> stacks,
) {
List<DocumentReference> newStacks = [];
for (var i = 0; i < stacks.length; i++) {
stacks[i].get().then((DocumentSnapshot stackSnapshot) {
if (stackSnapshot.exists) {
DocumentReference thisstack = stackSnapshot['player'];
for (var x = 0; x < favoritePlayers.length; x++) {
if (favoritePlayers[x] == thisstack) {
newStacks.add(stacks[i]);
}
}
}
});
if (stacks.length - 1 == i) {
return newStacks;
}
}
}
I do not have the option in the platform I am using to make the function async or Future, so I am trying to figure out a way to not return until both for loops are done. I have tried while loops and do while, but I guess I am missing something, this method is close, but it seems it still finishes before the inner loop does and wants to return nothing.
Any thoughts would be great.

In your code, the loop goes to the next operation without waiting for "stacks[i].get()" .
If the "stacks[i].get()" function is not a Future function, you can use it as follows.
List getStacks( List favoritePlayers, List stacks) {
List newStacks = [];
for (final stack in stacks) {
DocumentSnapshot stackSnapshot = stack.get();
//code
newStacks.add(stackSnapshot);
}
return newStacks;
}
If it is a future function, it should be like this;
Future<List> getStacks( List favoritePlayers, List stacks) async{
List newStacks = [];
for (final stack in stacks) {
DocumentSnapshot stackSnapshot = await stack.get();
//code
newStacks.add(stackSnapshot);
}
return newStacks;
}

Related

Dart: CloudFirestore: Firebase -> How to pass firestore query operator as variable Query<T>.where(key, operator, value)

Firestore with Dart requires query operators to be passed as objects.
Docs: https://firebase.google.com/docs/firestore/query-data/queries
Ex Dart:
.where("key", isEqualTo: value)
Ex Go:
.where("key", "==", value).
In the case of go, passing a string "==" as the query operator is pretty straight forward.
For dart, i am trying to figure out how to store isEqualTo: as a variable, to then pass into the function.
Ok here is my code. Any help is really appreciated thank you!
Here is the DataModel
class FirestoreQueryModel extends Equatable {
//Variables
final String property;
final FirestoreOperatorEnum operator;
// This is the value where i want to store the list of operators
final dynamic value;
}
Here is the current repository
class CustomerRepository{
late Query golbalQuery
Stream <List<CustomerModel>> dynmamicCollectonStream(List<FirestoreQueryModel> queryList,) { //If query list is null, do not apply where clause
if (queryList.isEmpty) { return collectionRef().snapshots().map((doc) {var returnedList = doc.docs; var mappedList = returnedList.map((doc) => CustomerModel.fromDocument(doc)).toList();
return mappedList;
});
} else {var count = queryList.length; CollectionReference cRef = collectionRef();
for (var i = 0; i < count; i++) { golbalQuery = cRef.where(queryList[i].property, isEqualTo: queryList[i].value); }
var list = golbalQuery.snapshots().map((doc) { var returnedList = doc.docs; var mappedList = returnedList.map((doc) =\> CustomerModel.fromDocument(doc)).toList(); return mappedList; }); return list; } }}
In the for loop, where we convert the data model to a where clause I currently have hardcoded isEqualTo:
The goal is to convert
for (var i = 0; i < count; i++) { golbalQuery = cRef.where(queryList[i].property, isEqualTo: queryList[i].value); }
to
for (var i = 0; i < count; i++) { golbalQuery = cRef.where(queryList[i].property, qyeryList[i].operator, queryList[i].value); }

Flutter: Concurrent modification during iteration

So I know that my mistake lies in trying to change my data when im iterating through it
this is the exact error
"Expected Non-error `Concurrent modification during iteration:
Instance of 'LinkedMap<dynamic, dynamic>'.
and this is my code:
_products.forEach((key, value) {
if (key.titel == product.titel) {
_products[key] += 1;
} else {
_products[product] = 1;
}
}
but I don't really get how I would be able to modify my data without iterating through it. Any suggestions?
----------------my Attempt -------------------
var _products = {}.obs;
var temp = {}.obs;
void addProduct(Product product) {
temp = _products;
if (_products.length > 0) {
_products.forEach((key, value) {
if (key.titel == product.titel) {
temp[key] += 1;
} else {
temp[product] = 1;
}
});
}
I get the same error; I think that is because in this line:
temp = _products;
I just get the reference on _products & I don't write _products in temp
So as mmcdon suggested you need make a copy of your existing thing that you want to iterate upon & iterate through that instead of your original piece
So in my case I need to do this:
var _products = {}.obs;
var temp = {}.obs;
void addProduct(Product product) {
temp = RxMap<dynamic, dynamic>.from(_products);
if (_products.length > 0) {
temp.forEach((key, value) {
if (key.titel == product.titel) {
_products[key] += 1;
} else {
_products[product] = 1;
}
});
}
So as you can see I iterate through temp, that basically is _products & Im writing the data that I need in _products

Loop through all firestore documents in a collection AND update them in flutter

I need to iterate through all the documents in the "exercise" collection and update them with new values. I wrote a function that works but it's an inefficient solution. This is the whole function:
final thisWorkout = await FirebaseFirestore.instance
.collection('workouts')
.doc(widget.docRef!.id);
final allExercises = await thisWorkout.collection('exercise').get();
thisWorkout.update({
'name': workoutName,
});
List allIds = [];
allExercises.docs.forEach((DocumentSnapshot value) {
allIds.add(value.reference.id);
});
for (var i = 0; i < exercises.length; i++) {
thisWorkout.collection('exercise').doc(allIds[i]).set(exercises[i]);
}
This is the portion of the function that is inefficient:
List allIds = [];
allExercises.docs.forEach((DocumentSnapshot value) {
allIds.add(value.reference.id);
});
for (var i = 0; i < exercises.length; i++) {
thisWorkout.collection('exercise').doc(allIds[i]).set(exercises[i]);
}
Is there a way to just iterate through each doc AND update it without having store all the ids then run a separate for loop for the update?
You mean this?
allExercises.docs.forEach((DocumentSnapshot value) {
thisWorkout.collection('exercise').doc(value.reference.id).set(exercises[i]);
});

Dart: How to create a future from multiple completers

I want to create a future from multiple completers in dart/flutter.
Future getAndWriteToDB(conversations){
List<Completer> _completerList = List();
for (var i = 0; i < conversations.length; i++) {
final conversation = conversations[i];
_completerList.add(Completer());
SocketService()
.listenForEvent('mylistenerkey')
.then((data) {
_messagesDao.insertMessages(messages).then((result) {
if (result == false) {
print("writing to db for message failed");
} else {
_completerList[i].complete(true);
}
});
}
});
}
return {{ wait for these completers complete event }}
}
Here my SocketService().listenForEvent gets called whenever that event is available. I want to write all of my conversations to database then once all those are completed get to know that state.
So I need to wait for or create a future based on these list of completers. How can I achieve this?
You can use Future.wait:
return Future.wait(_completerList.map((e) => e.future));

How to improve speed of query for Firestore + Flutter?

I have Flutter + Firestore app with a perfomance problem: large database query execution time (about a 5 sec.). I have a small database size, I think that if it increases, the query execution speed will be even greater. How can I improve application performance?
import 'package:carstat/models/entry.dart';
import 'package:carstat/models/operation.dart';
import 'package:carstat/services/data_service.dart';
class DashboardService {
DataService dataService = DataService();
getMarkers(List<Entry> entries, String carId) async {
var _markers = [];
for (int i = 0; i < entries.length; i++) {
List<Operation> _operations = [];
_operations =
await dataService.getEntryOperations(entries[i].entryId, carId);
_markers.add({'entry': entries[i], 'operations': _operations});
}
return _markers;
}
}
My data structure for example:
.document(docId)
.collection('cars')
.document(carId)
.collection('entries')
.document(entryId)
.collection('operations')
.document();
DataService code:
getEntryOperations(String entryId, String carId) async {
List<Operation> _operations = [];
Future<QuerySnapshot> _userDoc =
fs.where('userId', isEqualTo: _userId).getDocuments();
await _userDoc.then((res) {
docId = res.documents[0].documentID;
});
Future<QuerySnapshot> _entryOperations = fs
.document(docId)
.collection('cars')
.document(carId)
.collection('entries')
.document(entryId)
.collection('operations')
.getDocuments();
await _entryOperations.then((val) {
for (int i = 0; i < val.documents.length; i++) {
var _operation = Operation();
_operation.entryId = entryId;
_operation.operationNote = val.documents[i].data['operationNote'];
_operation.operationDate =
val.documents[i].data['operationDate'].toDate();
_operation.operationMileage = val.documents[i].data['operationMileage'];
_operation.operationPartName =
val.documents[i].data['operationPartName'];
_operation.operationPrice = val.documents[i].data['operationPrice'];
_operation.partPrice = val.documents[i]['partPrice'];
_operation.operationId = val.documents[i]['operationId'];
_operations.add(_operation);
}
});
return _operations;
}
The query you're showing is unconditionally getting all of the documents in a specific subcollection. Of course, that will take more time as the collection grows. There is no secret trick or special flag to pass to make this query happen any faster.
In fact, there is not much you can do about this at all, other than to limit the size of the collection, or limit the number of documents in the query. You might also want to reconsider your database structure to reduce the number of documents you're fetching.
My answer, much faster
class DashboardService {
DataService dataService = DataService();
getMarkers(List<Entry> entries, String carId) async {
var _marker = []; // коллекция списков операторов для каждого регламента ТО
final opsForEntries = await Future.wait(
entries.map((value) {
return dataService.getEntryOperations(value.entryId, carId);
})
);
for(int i = 0; i < entries.length; i++) {
_marker.add(
{
'entry': entries[i],
'operations': opsForEntries[i]
}
);
}
return _marker;
}
}