Create a list inside an item of another list - flutter

I have a list of teams to which the logged in user belongs to in my application. Within each team there is a list with the Uids of each team member (teamMembersUid). There is also an empty list in which the information of each user (teamMembers) must be inserted.
Objective: I want to take the Uids of each user in teamMembersUid, extract their information from the database (Firebase firestore) and introduce it into teamMembers list. Here is the team model with the lists:
class TeamModel {
String? uid;
String? teamName;
List<String>? teamMembersUid;
List<UserModel>? teamMembers;
List<String>? publicationsUid;
List<String>? notifications;
List<String>? instancesUid;
TeamModel(
{this.uid,
this.teamName,
this.teamMembersUid,
this.teamMembers,
this.instancesUid,
this.notifications,
this.publicationsUid});
//receiving data from server
factory TeamModel.fromMap(map) {
return TeamModel(
uid: map['uid'],
teamName: map['teamName'],
teamMembersUid: map['teamMembersUid'] is Iterable
? List.from(map['teamMembersUid'])
: null,
teamMembers:
map['teamMembers'] is Iterable ? List.from(map['teamMembers']) : null,
publicationsUid: map['publicationsUid'] is Iterable
? List.from(map['publicationsUid'])
: null,
notifications: map['notifications'] is Iterable
? List.from(map['notifications'])
: null,
instancesUid: map['instancesUid'] is Iterable
? List.from(map['instancesUid'])
: null,
//List.from(['teamMembersUid']), //castFrom adapts teamMembersUid to be a List
);
}
The following function (getTeamsInfoWithUsers) is responsible for:
Create a list with the teams to which the user belongs from firestore.
Extract the Uids of each member of each team.
Get the information from each user from firestore.
Create a list with the information of each user of each team and enter it in teamMembers.
UserModel userData;
List<TeamModel> retVal = [];
try {
//Create a list with the teams to which the user belongs from firestore
final data = await _firestore
.collection("teams")
.where("teamMembersUid", arrayContains: userUid)
.get();
List<TeamModel> data_m =
List.from(data.docs.map((doc) => TeamModel.fromMap(doc)));
//Extract the Uids of each member of each team.
retVal = data_m
.map((team) {
team.teamMembersUid!.map((userUid) async {
//Get the information from each user from firestore.
userData = await getUserInfo(userUid);
//Create a list with the information of each user of each team and enter it in teamMembers.
team.teamMembers!.add(userData);
});
})
.cast<TeamModel>()
.toList();
} catch (e) {
print(e);
}
return retVal;
}```
Problem: The current user belongs to 4 teams and each team has 3 users. Debugging I put a breakpoint in team.teamMembers!.add(userData); line. Since there are 4 teams and each team has 3 users you should see the code stop on that line 12 times, 3 for each team. Instead, it only stops 3 times and doesn't seem to create the list of teamMembers in the data_m variable either. I don't understand why it doesn't work.
Error:
Null error

how about this
retVal = data_m
.map((team) {
team.teamMembersUid!.map((userUid) async {
userData = await getUserInfo(userUid);
team.teamMembers!.add(userData);
}).toList();
})
.cast<TeamModel>()
.toList();
VS

Related

Does streambuilder from firebase rtdb will update list<User> user data?

currently I understadn with the method streamBuilder I can fetch updated data and add in the List<User> users.
But what if this user which is already added in the List<User> users has updated data, and then it could be double adding this user data in the List<User> users right?
Could you plz show me how to confirm whether for the new coming data List<User> users has already same userId, if yes, the new data / userId will replace this exisiting userId?
If the user is deleted from Firebase rtdb, the stream will be notified, and therefore remove this user from List<User> users?
here is example, my concern is since stream will always add data to the List users, but what if this user is removed from database or disconnect, how to remove this user from this list?
_streamSubscription = availableUserStream.onValue.listen((snap) {
if (snap.snapshot.exists && snap.snapshot.value != null) {
DataSnapshot snapshotData = snap.snapshot;
for (var userSnapshot in snapshotData.children) {
final data = Map<String, dynamic>.from(userSnapshot.value as Map);
List<User> users = [];
User newUser = User.fromJson(data);
users.add(newUser);
firebaseController.setUsers(users: users);
}
}
});
So I thought to do a double confirm here if this user is still exisitng in the database:
User getRandomSenderUser({User asReceiverUser}) {
if (availableSenderUsersList.isNotEmpty) {
final random = Random();
var i = random.nextInt(availableSenderUsersList.length);
User randomUser = availableSenderUsersList[i];
bool thisRandomUserIsAvailable; //TODO
I don't know how to do this check, e.g. if this randomerUser is unavailable, so I need to get next randomUser, so it should be a loop? But it will slow down the response speed.
updateSenderUserAvailableStatus(asReceiverUser:asReceiverUser,connectionUser: randomUser);
return randomUser;
} else {
return null;
}
}
thank you!
Update:
Here is the example code, so now I understand stream will pass user data to List<User> users, but in my way there will always be user who is added in this list before, but was already removed from database, my plan is using while loop for double confirming to remove unavailable user when getting the randomUser, but it sounds not smart and still waste time I guess....
#override
void initState() {
_listenAvailableUsers();
}
_listenAvailableUsers() {
var availableUserStream =
FirebaseDatabase.instance.ref().child('/waitingList');
_streamSubscription = availableUserStream.onValue.listen((snap) {
if (snap.snapshot.exists && snap.snapshot.value != null) {
DataSnapshot snapshotData = snap.snapshot;
for (var userSnapshot in snapshotData.children) {
final data = Map<String, dynamic>.from(userSnapshot.value as Map);
List<User> users = [];
User newUser = User.fromJson(data);
users.add(newUser);
firebaseController.setUsers(users: users);
}
}
});
}
Here is the method I though to confirm if the randomUser is still existing in the database:
Future<User> getRandomSenderUser({User asReceiverUser}) async {
if (availableSenderUsersList.isNotEmpty) {
User randomUser;
while (true) {
final random = Random();
var i = random.nextInt(availableSenderUsersList.length);
randomUser = availableSenderUsersList[i];
DatabaseEvent event = await databaseReference
.child('/waitingList/${randomUser.userId}')
.once();
print('randomUser is ${randomUser.toString()}');
if (event.snapshot.value != null) {
break;
}
}
await updateSenderUserAvailableStatus(
asReceiverUser: asReceiverUser, connectionUser: randomUser);
print('connectionUserId is $connectionUserId');
return randomUser;
} else {
return null;
}
}
Since you're listening to the onValue of a path in the database, the DataSnapshot you get will contain the entire data at that path. When there was only a small change in the data, the server will only send that update to the client, but the SDK will then merge that with the existing data and still fire an event with a snapshot of all the data at the path.
Since you're starting with an empty list (List<User> users = [];) each time you get an event from the stream, that means you're rebuilding the entire lit of users each time, which seems correct to me.

How to change index of an item in list in Flutter

I am working on a messaging app and I need help on a feature. Chat Users are stores inside a list and it is shown in a List View. When new message come to each chatuser(item in the list) I need it change that item as first. So recent contacts will be shown first like other social medias. I need to know how to change index of it to first. For swapping I need to take two items which in this case I cant do. So when new message comes to an item that item should be the first in that list. What should I do
have a look at this:
class Contact {
final List<String> messages;
final String name;
Contact({required this.name, this.messages = const []});
}
void main() {
List<Contact> contacts = [Contact(name: "userA"), Contact(name: "userB"), Contact(name: "userC")];
void receiveMessage(String username, String message) {
final Contact user = contacts.where((e) => e.name == username).first;
contacts.remove(user);
user.messages.add(message);
contacts.insert(0, user);
}
receiveMessage("userB", "hello");
receiveMessage("userC", "helloooo");
receiveMessage("userB", "hello000000000");
for (var contact in contacts) {
print(contact.name);
for (var message in contact.messages) {
print(message);
}
print("------------------------");
}
}

What would be the better way to write this dart iteration and return a list

The purpose is to do some sort of join. Get each user doc from the user document in firestore and attach it to some collection by uid. Then return the collection with the user document full details rather than just the uid.
Future<List<ChatRoom>> getChatsrooms(uid) async {
ChatRoom chatroom = new ChatRoom();
try {
List<ChatRoom> chatrooms = new List();
var snapshots = await _db
.collection('my-chats-chats')
.where('users', arrayContains: uid)
.orderBy('updated_at', descending: true)
.get();
for (var room in snapshots.docs) {
chatroom.chatroomid = room.data()['chatroomid'];
chatroom.users = room.data()['users'];
// get user profile that isn'nt u.
List<dynamic> userids = await room.data()['users']
..remove(uid);
var doc = await _db.collection('my-chats-users').doc(userids[0]).get();
UserModel user = UserModel.fromMap(doc.data());
chatroom.users.add(user);
// Remove the users string UID from users list
chatroom.users.remove(user.uid);
chatroom.users.remove(uid);
chatrooms.add(chatroom);
}
return chatrooms.toList();
} catch (e) {
print("Couldn't get user\'s chatrooms exception: " + e.toString());
return null;
}
}
The above seems to do what I want, except the chatrooms list returned, only contains duplicates of one chat room. So even there are 10 different chat rooms, I am only getting duplicates of one chatroom 10 times.
What I am thinking of is the chatrooms.add(chatroom); only adds one item. But the chatrooms list is supposed to be a growable list. So why is it ending up with just one duplicated item?
What would be the better way to write this?

FIrestore Query Document in Collection Using Single Field/Attribute in Flutter

I am trying to fetch the role of the currently authenticated user stored in users collection. What I am trying to achieve is at login time, query the user role by traversing fetching the user's document in the collection and sifting through the fields or checking all documents and returning the field role as a string.
Collection and document snapshot(excuse the terminology):
All documents in users collection have same fields for now.
Please how do I go about writing this type of query in flutter? I have tried using AuthResult in my service and FirebaseAuth to get current user(but no way to access the fields in the document).
Thanks.
String role;
getUserRoleWithFuture() async {
String currID = await _authService.getCurrentUID();
String mRole;
Firestore.instance.collection(USERS_REF).document(currID).get().then((doc) {
mRole = doc.data['role'];
print(mRole);
});
return mRole;
}
Future<String> getUserRoleWithStream() async {
String currID = await _authService.getCurrentUID();
String sRole;
Firestore.instance
.collection(USERS_REF)
.document(currID)
.snapshots()
.listen((DocumentSnapshot ds) {
if (ds.exists) {
sRole = ds.data['role'];
print('with stream:\t$sRole');
}
});
return sRole;
}
In the method getUserRoleWithStream() I am trying to retrieve the value printed out like role = getUserRoleWithStream() but instead get this in console a value of type Future<String> can't be assigned to a variable of type string.
How do I get this value using either the stream (cos it constantly observes the collection) or using the other method and use it in my widget?
Thanks again.
This is the working solution, in case anyone else runs into this. I appreciate the effort made into helping me understand the issue but here's the answer:
String role;
getUserRoleWithFuture() async {
String currID = await _authService.getCurrentUID();
String mRole;
Firestore.instance.collection(USERS_REF).document(currID).get().then((doc) {
mRole = doc.data['role'];
print(mRole);
});
return mRole;
}
Future<String> getUserRoleWithStream() async {
String currID = await _authService.getCurrentUID();
String sRole;
Firestore.instance
.collection(USERS_REF)
.document(currID)
.snapshots()
.listen((DocumentSnapshot ds) {
if (ds.exists) {
sRole = ds.data['role'];
print('with stream:\t$sRole');
}
});
return sRole;
}
Well first off, I assume the AuthResult.user.uid and your user's collection user's id is same. So that once you have the user from AuthResult, you can query your firestore collection to get the user's role as follows.
Future<String> getUserRole(String uid) async {
DocumentSnapshot ds = await Firestore.instance.collection('users').document(uid).get();
return ds.data['role'];
}

How to upload contacts as Iterable from Flutter to Firestore

I'm creating an app which will upload all contacts to Firestore database with map type. But below code do not upload contacts to Firestore. Please help.
I have been trying to fetch contacts from my phone to Firestore using Flutter app. I used contact_services library to fetch contacts. Even after trying some similar examples, I could not my contacts to Firestore using map type. Where do I make changes so that I can upload all my contacts as map value to Firestore
final Iterable<Contact> contacts = await ContactsService.getContacts(withThumbnails: false);
Firestore.instance
.collection('contacts')
.document(firebaseUser.uid)
.setData({
'contact':{ contacts.map((Contact contact){ 'name': contacts.displayName,contacts.phone})}
});
I expected to display all my contacts in firestore with map type, but actual output is none were uploaded.
I am sure you have fixed this since this is an old post. But there are a few things incorrect with how you are using the map method id Dart.
You named your element variable "contact" and not "contacts" so that is the variable you should be referencing "contact" to get your information. So it would be contact.displayName.
You are not returning anything. Because there is no "=>" there is no implicit return and because there is no "return" there is no explicit return.
Also, what you are returning from the map method is an Iterable to the contact field.
I am not sure what you are trying to accomplish here. Are you trying to insert a nested object? If so, your phone is also missing a key
Little late but, this is for the reference for the Firestore version ^0.16.0.
// your list of contacts in _contacts.
List<Contact> _contacts = [];
final FirebaseFirestore _db = FirebaseFirestore.instance;
CollectionReference _ref = _db.collection(collectionName);
Future<void> syncContacts({uid}) async {
try {
Map<String, dynamic> _data;
if (_contacts != null)
_data = {
'contacts': _contacts
.map((k) => {
'name ': k.displayName,
'phone': k.phones.first.value
.toString()
.replaceAll(new RegExp(r"\s\b|\b\s"), "")
.replaceAll(new RegExp(r'[^\w\s]+'), '')
})
.toList(),
};
log(_data.toString());
await _service.reference().doc(uid).set(_data, SetOptions(merge: true));
} catch (e) {
log(e.toString());
} finally {
notifyListeners();
}
}
P.S I have used flutter_contact library to get contacts from the device.