Flutter Firebase Chat App – Part 5: Message Status System (Sent, Delivered, Read)

Learn how to implement message status indicators like Sent, Delivered, and Read in a Flutter chat app using Firebase Firestore.

Introduction

Modern messaging apps provide visual feedback for message delivery. Users expect to know whether their message has been sent, delivered, or read.

In this article we will implement a message status system similar to WhatsApp.

The three states are:

  • Sent
  • Delivered
  • Read

Message Status Flow

User sends message
      ↓
Message saved in Firestore
      ↓
Receiver device receives message
      ↓
Status updated to delivered
      ↓
Receiver opens chat
      ↓
Status updated to read

Firestore Message Document Structure

{
  "senderId": "user1",
  "text": "Hello",
  "createdAt": Timestamp,
  "status": "sent"
}

Possible status values:

  • sent
  • delivered
  • read

Step 1: Set Initial Status When Sending

await messageRef.set({
  "senderId": currentUserId,
  "text": text,
  "createdAt": Timestamp.now(),
  "status": "sent"
});

Step 2: Update Status to Delivered

When receiver device loads the message stream, update status to delivered.

await messageRef.update({
  "status": "delivered"
});

Step 3: Mark Messages as Read

When receiver opens the chat screen:

QuerySnapshot messages =
  await FirebaseFirestore.instance
    .collection('conversations')
    .doc(conversationId)
    .collection('messages')
    .where('status', isEqualTo: 'delivered')
    .get();

for (var doc in messages.docs) {
  doc.reference.update({
    "status": "read"
  });
}

Message Status UI Icons

if (message.status == "sent") {
  Icon(Icons.check)
}

if (message.status == "delivered") {
  Icon(Icons.done_all)
}

if (message.status == "read") {
  Icon(Icons.done_all, color: Colors.blue)
}

WhatsApp Style Status Meaning

  • ✓ Message sent to server
  • ✓✓ Message delivered to device
  • ✓✓ Blue Message read

Only Show Status for Sender

if (message.senderId == currentUserId) {
  showStatusIcon();
}

Performance Optimization

  • Batch update read messages
  • Avoid updating each message individually

Batch Update Example

WriteBatch batch = FirebaseFirestore.instance.batch();

for (var doc in messages.docs) {
  batch.update(doc.reference, {"status": "read"});
}

await batch.commit();

Real-Time UI Update

Since we are using Firestore streams, message status updates automatically appear in the UI.

Prevent Updating Own Messages

if (message.senderId != currentUserId) {
  updateStatus();
}

Scaling Strategy

  • Update status only for visible messages
  • Avoid updating very old messages

Security Rules Example

match /conversations/{conversationId}/messages/{messageId} {
  allow update: if request.auth != null;
}

Common Mistakes

  • Updating read status too frequently
  • No batching
  • Updating messages not belonging to user

Production Flow Summary

Message sent
     ↓
Status = sent
     ↓
Receiver device loads message
     ↓
Status = delivered
     ↓
Receiver opens chat
     ↓
Status = read

Conclusion

Adding message status indicators greatly improves the user experience of a messaging app.

Users feel confident knowing their messages have been delivered and read.

In Part 6, we will implement: Typing Indicator (User is typing...).

Share

What's Your Reaction?

Like Like 0
Dislike Dislike 0
Love Love 0
Funny Funny 0
Angry Angry 0
Sad Sad 0
Wow Wow 0