How to change index of an item in list in Flutter - 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("------------------------");
}
}

Related

Flutter listeners keep listening to old events every time the page is rebuilt

I am currently developing an order management app for a restaurant, in which users can place orders through the app. Once orders are placed, an admin (restaurant manager) can accept orders on the admin portal, and the user will be notified to go pick up their food when the order is completed.
I am using flutter's "awesome notifications" package to handle notifications.
In the method below, I am essentially listening for new entries into the "ManagedOrders" table of our database (firebase real time database). Accepted orders are moved into this table, therefore I want to notify the user that their order has been accepted if the order moved into this table contains a customerID equal to the customerID of the user currently logged in.
import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import '../models/notifications.dart';
final DatabaseReference _dbRef = FirebaseDatabase.instance.ref();
late StreamSubscription _orderStream;
String loggedInUserID = "9ibdsUENaAdnpA3qxm35Y8xRe9F3"; //Hard coded for now
Map<dynamic, dynamic> databaseMapper = {};
List<String> placedOrderIDsList = [];
void listenForAcceptedOrders() async {
//This method listens for accepted orders and displays a notification
//It checks for new records entered into the managedOrders table containing the same customerID as the user currently logged in.
//If the customerID matches the ID of the current user logged in, it means that an order placed by THIS user has been accepted.
_orderStream = _dbRef.child("ManagedOrders").onChildAdded.listen((event) {
databaseMapper = event.snapshot.value as Map;
String customerID = databaseMapper["customerID"].toString();
print("CUSTOMERID ______......>>" + customerID);
if (customerID == loggedInUserID) {
acceptedOrderNotification(); //A notification defined in another class
} else {
print("NO MATCH FOUND");
}
});
//_orderStream.cancel();
}
I then call this method in my homescreen in the initState method:
listenForAcceptedOrders();
The issue I am having is once a record is added to the "ManagedOrders" table with a customerID that matches the ID of the user logged in, I continue to receive the notification every time I navigate back to the home page, even if I delete the record from the "ManagedOrders" table.
I tried to cancel the listeners at the end of the "listenForAcceptedOrders()" method, but that results in no listeners at all (I.E adding a record to the "ManagedOrders" table does not trigger any notification).
How do I make it so that the user can receive the notification once, and not have it repeat every time they navigate to the home page?
Any help would be much appreciated!
just add a bool variable and set it to true once you receive the notification so it will not repeat it self again
Look the correct code below:
import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import '../models/notifications.dart';
final DatabaseReference _dbRef = FirebaseDatabase.instance.ref();
bool example = false;
late StreamSubscription _orderStream;
String loggedInUserID = "9ibdsUENaAdnpA3qxm35Y8xRe9F3"; //Hard coded fornow
Map<dynamic, dynamic> databaseMapper = {};
List<String> placedOrderIDsList = [];
void listenForAcceptedOrders() async {
_orderStream = _dbRef.child("ManagedOrders").onChildAdded.listen((event) {
databaseMapper = event.snapshot.value as Map;
String customerID = databaseMapper["customerID"].toString();
print("CUSTOMERID ______......>>" + customerID);
if (customerID == loggedInUserID) {
if(example = false){
acceptedOrderNotification(); //A notification defined in another class
example = true;
}
} else {
print("NO MATCH FOUND");
}
});
//_orderStream.cancel();
}

Create a list inside an item of another list

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

Flutter - make api call inside Listview

I am calling an api, which will return a list of items (workouts), and items will be shown in a ListView. but I also need to call another api for each item of the ListView to fetch the score of that workout (if there is already a score) and show the score to each workout according to their id. How can I resolve this scenario.? I am using bloc and this is how I am fetching the workouts
Future<void> _onLoadingWod(
OnLoadingWodEvent event, Emitter<WhiteboardState> emit) async {
emit(state.copyWith(isWodLoading: true));
final listWods =
await WhiteboardService().getListWod(state.selectedDay.toString());
emit(state.copyWith(
isWodLoading: false, listWods: listWods));
}
some workouts may not have a score yet, in that case, I show in the listview a button to log a score for each workout.
I tried something like this:
final listResults = await Stream.fromIterable(listWods)
.asyncMap((wod) => WhiteboardService().getresult(wod.id))
.toList();
but since some workouts may not have result, is showing error.
how can a link workouts with results and show then in a listview? pls any help o guide is appreciated.
As I understand, it can be done from a loop. You have to create a model class for results that include the workout id. Then you can loop through it and find the relevant workout from the workout list and then assign the results for it.
there is an example hope it will help.
*dummy model classes
class Workouts {
final int id;
Result? result;
Workouts({
required this.id,
this.result,
});
}
class Result {
final int workoutId;
final String result;
Result({
required this.workoutId,
required this.result,
});
}
*assign results to workout list
void asignResults() {
///for each [results]
for (Result result in listResults) {
listWorkouts
.firstWhere((workout) => workout.id == result.workoutId)
.result = result;
}
listWorkouts.forEach((element) {
print(element.toString());
});
}
I think if you can do this from the backend it will be more efficient than this approach. (eg:- join query in sql)

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 get contact number from contact_service Flutter

I'm useing contact_service to manage my contacts. I have fetch all contacts as a list. And I want to access fields with phone number of each contact. I want to get it as a string but the atribute in Contact class is
Iterable<Item> phones
Do You know how can I get a phone number from this ?
Link to this package :
https://pub.dev/packages/contacts_service
Thanks in advance
For every Iterable<Item> phones, Item.value returns the phone number string.
List<String> names = [];
List<String> phones = [];
Iterable<Contact> _contacts = await ContactsService.getContacts(withThumbnails: false);
_contacts.forEach((contact) {
contact.phones.toSet().forEach((phone) {
names.add(contact.displayName ?? contact.givenName);
phones.add(phone.value);
});
});
List<Contact> _contacts;
Future<void> refreshContacts() async {
// Load without thumbnails initially.
var contacts = (await ContactsService.getContacts(
withThumbnails: false, iOSLocalizedLabels: iOSLocalizedLabels))
.toList();
setState(() {
_contacts = contacts;
});
}
and use this contact list to render widget inside ListView/Column
Follow complete example here
https://github.com/lukasgit/flutter_contacts/blob/master/example/lib/contacts_list_page.dart