Breaking changes with cloud_firestore 2.0? - flutter

I am using CloudFirestore with my app.
Everything was working fine and since the 2.0.0 version, I encounter errors that I didn't before.
Here is the code :
final _fireStore = FirebaseFirestore.instance
.collection('familyAccounts')
.doc(id)
.collection('users');
final DocumentSnapshot doc1 = await _fireStore.doc('user1').get();
final DocumentSnapshot doc2 = await _fireStore.doc('user2').get();
final _fireStore2 = FirebaseFirestore.instance
.collection('familyAccounts')
.doc(id)
.collection('users')
.doc('user1')
.collection('vocList');
await _fireStore2.get().then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
_carnetVoc1.add(
VocList(
ref: doc['ref'],
titre: doc['titre'],
creation: doc['dateCreation'],
modification: doc['dateModification'],
wordId: doc['mots']),
);
});
});
final _fireStore3 = FirebaseFirestore.instance
.collection('familyAccounts')
.doc(id)
.collection('users')
.doc('user2')
.collection('vocList');
await _fireStore3.get().then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
_carnetVoc2.add(
VocList(
ref: doc['ref'],
titre: doc['titre'],
creation: doc['dateCreation'],
modification: doc['dateModification'],
wordId: doc['mots']),
);
});
});
_accountEmail = id;
Map user1 = doc1.data()!;
Map user2 = doc2.data()!;
_user1 = User(
userId: user1['userId'],
avatar: user1['avatar'],
classe: user1['classe'],
teacherCode: user1['teacherCode'],
carnetVoc: _carnetVoc1);
_user2 = User(
userId: user2['userId'],
avatar: user2['avatar'],
classe: user2['classe'],
teacherCode: user2['teacherCode'],
carnetVoc: _carnetVoc2);
The lines :
Map user1 = doc1.data()!;
Map user2 = doc2.data()!;
no longer work with the new version : I get this :
"A value of type object can't be assigned to a variable of type Map".
I don't understand what has changed... as all this was working fine before.
Anyone encountered this too ?

there is a document to perform the migration: https://firebase.flutter.dev/docs/firestore/2.0.0_migration/
Referring to it, you should add type <Map<String, dynamic>> explicitly.
In your case you need to change:
final DocumentSnapshot doc1 = await _fireStore.doc('user1').get();
final DocumentSnapshot doc2 = await _fireStore.doc('user2').get();
to:
final DocumentSnapshot<Map<String,dynamic>> doc1 = await _fireStore.doc('user1').get();
final DocumentSnapshot<Map<String,dynamic>> doc2 = await _fireStore.doc('user2').get();
Also, cloud_firestore: 2.0.0 promotes type safety, hence I'd suggest you using Map concrete types for your variables:
Map<String,dynamic> user1 = doc1.data()!;
Map<String,dynamic> user2 = doc2.data()!;

Related

Toggling a favorites button while adding and deleting items from firestore

I want to toggle a favourites button using firestore. When i click on it initially, I want to add it to a collection, then clicking again should remove that exact item from the collection. My issue here is that, I could achieve this by setting the doc name to the title and simply delete using the title has reference. This would have an issue later on, since two products can technically have the same name. That is why I want to use the id has a reference name since it is dynamic. This is the code below.
try {
if (widget.product['isFavourited'] == true) {
String docId = widget.product.id;
// print(docId.toString());
// print(widget.product['isFavourited'].toString());
await FirebaseFirestore.instance
.collection('Products')
.doc(docId)
.update({'isFavourited': false}).then((value) async {
final CollectionReference collectionReference =
FirebaseFirestore.instance
.collection('Users')
.doc(uid)
.collection('Favourites');
await collectionReference
.doc()
.delete();
print('');
});
} else {
String docId = widget.product.id;
await FirebaseFirestore.instance
.collection('Products')
.doc(docId)
.update({'isFavourited': true}).then((value) async {
final CollectionReference collectionReference =
FirebaseFirestore.instance
.collection('Users')
.doc(uid)
.collection('Favourites');
final String id = collectionReference.doc().id;
final String itemtodelete = collectionReference.doc(id).id;
print(itemtodelete);
print(id);
await collectionReference.doc(itemtodelete).set({
'id': itemtodelete,
'title': widget.product['title'],
'price': widget.product['price'],
'about': widget.product['about'],
'description': widget.product['description'],
'imagepath': widget.product['imagepath'],
'isFavourited': widget.product['isFavourited'],
'isCarted': widget.product['isCarted'],
});
});
try is out hope this will help.
// productId is your productId = widget.product.id
// uid is your current user id
// favorite id is your each favorite documentId
Future<void> addToFavorite(String productId, String uid, favoriteId) async {
final productCollection = FirebaseFirestore.instance.collection("Products");
final favoriteCollection = FirebaseFirestore.instance.collection("Users").doc(uid).collection("Favorites");
final productDocReference = await productCollection.doc(productId).get();
if (productDocReference.exists) {
final favoriteDocReference = favoriteCollection.doc(favoriteId).get().then((favoriteDoc) {
if (!favoriteDoc.exists) {
favoriteCollection.doc(favoriteId).set({
'id': favoriteId,
'title': widget.product['title'],
'price': widget.product['price'],
'about': widget.product['about'],
'description': widget.product['description'],
'imagepath': widget.product['imagepath'],
'isFavourited': widget.product['isFavourited'],
'isCarted': widget.product['isCarted'],}).then((value){
productCollection.doc(productId).update({
"isFavourited": true
});
});
} else {
favoriteCollection.doc(favoriteId).delete().then((value) {
productCollection.doc(productId).update({
"isFavourited": false
});
});
}
});
}
}
This code worked perfectly for me, given the way I structured my function. #Adnan Khan's answer was really helpful in figuring it out. I hope this helps someone who is stuck.
Future<String> addtoFavourites(QueryDocumentSnapshot data, String uid) async {
final CollectionReference collectionReference = FirebaseFirestore.instance
.collection('Users')
.doc(uid)
.collection('Favourites');
final String id = collectionReference.doc().id;
String docId = data.id;
try {
if (data['isFavourited'] == true) {
await FirebaseFirestore.instance
.collection('Products')
.doc(docId)
.update({'isFavourited': false}).then((value) async {
print(docId);
await collectionReference.doc(docId).delete();
return 'Removed from favourites';
});
} else {
await FirebaseFirestore.instance
.collection('Products')
.doc(docId)
.update({'isFavourited': true}).then((value) async {
print(docId);
print(id);
await collectionReference.doc(docId).set({
'id': docId,
'title': data['title'],
'price': data['price'],
'about': data['about'],
'description': data['description'],
'imagepath': data['imagepath'],
'isFavourited': true,
'isCarted': data['isCarted'],
});
});
}
notifyListeners();
return 'Added to favourites';
} catch (e) {
return e.toString();
}

async function not completing when querying FirebaseFirestore

See the print statement down below. It never executes.
Future<void> populate() async {
final userId = FirebaseAuth.instance.currentUser!.uid;
final db = FirebaseFirestore.instance;
// Get list of ids of parties use swiped on.
var snapshot1 = await db
.collection("partiers_swipes")
.where('userId', isEqualTo: userId)
.get();
var partyIdsUserSwipesOn = [];
if (snapshot1.size > 0) {
snapshot1.docs.forEach((element) {
partyIdsUserSwipesOn.add(element.data()['partyId']);
});
}
var snapshot2 = await db
.collection("parties")
.where(FieldPath.documentId, whereNotIn: partyIdsUserSwipesOn)
.get();
print('This never executes');
}
The whereNotIn argument is not supported by the where clause. This crashes the function.

Collecting string from Firebase Firestore in flutter

I am creating like system and i want to get likeCount from firebase which i created.
It's collecting it but returns null,
here is my code:
String? getLikecount(tresc) {
String? likeCount;
FirebaseFirestore.instance
.collection('Posty')
.where('Tresc', isEqualTo: tresc)
.get()
.then((value) => value.docs.forEach((element) async {
var id = element.id;
final value = await FirebaseFirestore.instance.collection('Posty').doc(id).get();
likeCount = value.data()!['likeCount'].toString();
print(likeCount);
}));
print(likeCount);
return likeCount;
}
and here is console output:
Data is loaded from Firestore (and most modern cloud APIs) asynchronously, because it may needs to come from the network and we can't block your code (and your users) while waiting for it.
If we change the print statements a bit, and format the code, it'll be much easier to see what's going on:
String? getLikecount(tresc) {
String? likeCount;
FirebaseFirestore.instance
.collection('Posty')
.where('Tresc', isEqualTo: tresc)
.get()
.then((value) => value.docs.forEach((element) async {
var id = element.id;
final value = await FirebaseFirestore.instance
.collection('Posty')
.doc(id)
.get();
likeCount = value.data()!['likeCount'].toString();
print('In then: $likeCount');
}));
print('After then: $likeCount');
return likeCount;
}
If you run this, you'll see it outputs:
After then: null
In then: 0
This is probably not what you expected, but it explains perfectly why you don't get a result. By the time your return likeCount runs, the likeCount = value.data()!['likeCount'].toString() hasn't executed yet.
The solution is always the same: any code that needs the data from the database has to be inside the then handler, be called from there, or be otherwise synchronized.
In Flutter it is most common to use async and await for this. The key thing to realize is that you can't return something now that hasn't been loaded yet. With async/await you function becomes:
Future<String?> getLikecount(tresc) {
String? likeCount;
var value = await FirebaseFirestore.instance
.collection('Posty')
.where('Tresc', isEqualTo: tresc)
.get();
for (var doc in value.docs) {
var id = element.id;
final value = await FirebaseFirestore.instance
.collection('Posty')
.doc(id)
.get();
likeCount = value.data()!['likeCount'].toString();
print('In then: $likeCount');
}));
print('After then: $likeCount');
return likeCount;
}
Now your code returns a Future<String?> so a value that at some point will hold the string. When calling getLikecount you will now need to use then or await to handle the Future, and if you want to show the count in the UI you will have to store it in the State of a StatefulWidget.

Firestore data not getting stored in Flutter

I'm trying to store the user data in firestore, when I'm using the phone number as the document id it's getting stored perfectly but as soon as I changed it to uid it's not. And there were no exceptions been thrown at either. Also I checked if the uid is empty or not too and It's not empty either.
Future addUserToFirestore(String uid) async {
final docUser = FirebaseFirestore.instance
.collection('users')
.doc(uid);
final user = u.User(
name: widget.fullName,
email: widget.email,
gender: widget.gender,
nic: widget.nic,
phoneNumber: widget.phone,
bloodType: widget.bloodType,
dateOfBirth: widget.dateOfBirth,
address: widget.address,
age: int.parse(widget.age));
final json = user.toJson();
await docUser.set(json);
}
When I use .doc('+94${widget.phone}'); instead of .doc(uid); it works fine. But I want to use the uid as document id. Is there a way to get this done?
Try the following method.
Hopefully, this will help.
Future<void> storeUser () async {
var instance = FirebaseFirestore.instance;
await instance.collection("users").doc("uid").set({
// add all of the user data here.
});
}
Else also try:
Future addUserToFirestore(String uid) async {
// add await here as well
final docUser = await FirebaseFirestore.instance
.collection('users')
.doc(uid);
final user = u.User(
name: widget.fullName,
email: widget.email,
gender: widget.gender,
nic: widget.nic,
phoneNumber: widget.phone,
bloodType: widget.bloodType,
dateOfBirth: widget.dateOfBirth,
address: widget.address,
age: int.parse(widget.age));
final json = user.toJson();
await docUser.set(json);
}

How to convert map into array to firestore? flutter

I have a users id I want to add it to firestore, like this
['GEcuHm3ICpWlEzfq1Z2tAjI2LII3', 'GEcuHm3ICpWlEzfq1Z2tAjI2LII3' ...]
I tried multiple ways but it didn't work
List membersListUid = [];
Future createGroup() async{
GroupRoomModel newGroup = GroupRoomModel(
groupName: groupName.text,
groupRoomId: uuid.v1(),
owner: userModel.uid,
membersList: controller.membersList,
membersListUid: controller.membersListUid.cast() // <---
);
}
...
Future createGroupFunc() async{
GroupRoomModel newGroup = GroupRoomModel(
groupName: groupName.text,
groupRoomId: uuid.v1(),
owner: userModel.uid,
membersList: controller.membersList,
membersListUid: controller.membersListUid.map((e)=> e).toList() //<---
);
...
Maybe this helps to understand the code
//Controller class
Map<String, dynamic>? userMap;
onSearch() async {
await _fireStore
.collection('users')
.where("email", isEqualTo: searchedMembers.text)
.get()
.then((value) {
userMap = value.docs[0].data();
});
update();
}
membersListUid.add({
"uid": userMap!['uid']
});
It's still gives me map within array.
THE PROBLEM:
membersListUid is a List of Maps. That is why you get an array of Maps in your database.
You need to get the actual value of the uid from each Map by using the uid key to get the value from the map.
THE SOLUTION:
Update this line:
membersListUid: controller.membersListUid.map((e)=> e).toList()
to this below:
controller.membersListUid.map((e)=> (e as Map<String, dynamic>)['uid']).toList()