Reputation: 2792
I am trying to create Read and Unread Message feature in my chat application.
So far I can create only Unread, which means when I send Message to firebase collection, I decleared my
READ
property field as FALSE
,
I am confused how the second user will change the value of the "READ: TRUE" back if he checks the chat room, and if the second user is currently in the chat room he should still change the READ: true.
here is the my send message data:
sender_id:12233,
reciever_id: 6767,
message:'hello please help',
read: false,
here is my code
StreamBuilder(
stream: firestore
.collection('chat')
.doc(widget.peerid)
.collection('Messages')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.data.docs.isEmpty) {
return Center(
child: Column(
children: [
Container(
child: Icon(
FontAwesomeIcons.comments,
size: 40,
),
),
Text(
'Say Hello',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
)
],
),
);
}
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
final authid =
snapshot.data.docs[index].data()['idFrom'];
final msg =
snapshot.data.docs[index].data()['content'];
bool check =
authid == auth.currentUser.uid ? true : false;
return Padding(
padding: EdgeInsets.symmetric(
horizontal: 15,
vertical: 10,
),
child: Column(
crossAxisAlignment: check
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: check
? Colors.indigo
: Colors.white,
borderRadius: check
? BorderRadius.only(
bottomLeft: Radius.circular(30),
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
)
: BorderRadius.only(
bottomRight:
Radius.circular(30),
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: 150),
child: Column(
children: [
Text(
msg,
style: GoogleFonts.raleway(
textStyle: TextStyle(
color: check
? Colors.white
: Colors.black,
),
),
),
Text('')
],
),
),
)
],
),
);
});
}),
),
),
Container(
height: Platform.isIOS ? 95 : 80,
padding: EdgeInsets.only(top: 8.0),
decoration: BoxDecoration(
borderRadius:
BorderRadius.only(topRight: Radius.circular(70)),
color: Theme.of(context).backgroundColor,
),
child: ListTile(
leading: Icon(
Icons.add,
color: Theme.of(context).primaryColor,
),
title: TextFormField(
controller: messagesController,
onChanged: (value) {
messagesController.text = value;
},
decoration: InputDecoration(
hintText: 'Enter your messgae here...',
border: InputBorder.none,
),
maxLines: null,
),
trailing: messagesController.text.trim() == null
? Container(
width: 40,
height: 45,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Constants.color1,
Constants.color2,
],
),
),
child: GestureDetector(
onTap: () async {
await ControllerApi().sendMessage(
content: messagesController.text.trim(),
chatID: widget.peerid,
messageType: 'text',
myID: auth.currentUser.uid,
selectedUserID: widget.userid,
);
},
child: Icon(
Icons.send,
size: 20,
color: Colors.white,
),
),
)
: Container(),
),
)
],
Thanks.
Upvotes: 1
Views: 7357
Reputation: 546
Generally, it's not correct to keep this mark in the same table. Because you need to add access rights sender and receiver to change all messages. Better solution is to create another table something like _metadata and keep this mark there.
Upvotes: 1
Reputation: 1156
Use this flutter library visibility_detector
Please see the below code, it will help you to implement the read & unread message.
@override
Widget build(BuildContext context) {
return ListView.separated(
itemBuilder: (ctx, index) {
ChatModel chatModel =
ChatModel.fromJson(snapshot.data.docs[index].data());
switch (chatModel.messageType) {
case MessageType.TEXT:
{
return VisibilityDetector(
key: Key(snapshot.data.docs[index].id),
onVisibilityChanged: (VisibilityInfo visibilityInfo) {
var visiblePercentage = visibilityInfo.visibleFraction * 100;
if (visiblePercentage == 100 &&
!chatModel.isSeen &&
chatModel.sender != userController.userModel.value.uid) {
FirebaseFirestore.instance
.collection(FirebaseKeys.chatRoom)
.doc(AppHelper.getChatID(
userController.userModel.value.uid, userModel.uid))
.collection(FirebaseKeys.messages)
.doc(snapshot.data.docs[index].id)
.update({
"isSeen": true,
});
}
},
child:
TextMessage(chatModel, snapshot.data.docs[index].reference),
);
}
case MessageType.GIF:
{
return GifMessage(chatModel, userModel.uid);
}
case MessageType.IMAGE:
{
return ImageMessage(snapshot.data.docs[index].reference,
chatModel, userModel.uid);
}
case MessageType.AUDIO:
{
break;
}
case MessageType.VIDEO:
{
break;
}
case MessageType.PDF:
{
break;
}
case MessageType.FILE:
{
break;
}
case MessageType.OTHER:
{
break;
}
}
return FlutterLogo();
},
shrinkWrap: true,
itemCount: snapshot.data.docs.length,
controller: scrollController,
reverse: true,
physics: ClampingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return Container(
height: 5,
margin: EdgeInsets.only(top: 2, bottom: 2),
);
},
);
}
Upvotes: 0
Reputation: 891
let's write a function that marks the opponent's messages as read.
Future<void> seeMsg(int peerId) async{
final query = await FirebaseFirestore.instance
.collection('chat')
.doc(peerId)
.collection('Messages')
.where('sender_id', isEqualTo: peerId)
.where('read', isEqualTo: false)
.get();
query.docs.forEach((doc) {
doc.reference.update({'read': true});
});
}
Then, call this function inside StreamBuilder
body.
Every time, a new message is generated inside this chat collection, this function checks for unread messages from your peer and marks them as read.
Upvotes: 2
Reputation: 3136
Inside your chat app, when you load messages to user, lets say you already have loaded messages and have a List<Message> messages
.
Lets say you show messages with ListView.builder
:
return ListView.builder(
shrinkWrap: true,
controller: _controller,
reverse: true,
itemCount: messages.length,
itemBuilder: (context, index) {
final message = messages[index];
// We check if message is new (dont know your entity so lets say it has boolean `read`, that false when new.
final bool isNew = !message.read;
// Given that you store your fire id in myId variable we check if message is received and not sent.
final bool toMe = message.recieverId == myId;
// Check that we need to mark message as read
if (isNew && toMe) {
return FutureBuilder<void>(
future: markAsRead(), // markAsRead is a Future function to change your FIELD value in firestore.
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
return YourMessageWidgetThatWasSeen()
}
return YourMessageWidgetThatWasNotSeen();}); //FutureBuilder ends here.
return MessageBubble(); // or this message is already seen by user and we return message bubble like usually do.
markAsRead()
function implementation depends on what structure your database has, for example when your each user has its own collection of messages to store, AND if you have a collections for a dialog preview with last message shown, you will need to create a batch with 4 update operations in it.
Upvotes: 0