Messaging

Learn how to send and receive messages, mark messages as delivered or read, etc.

Before you begin

  1. Register a QuickBlox account. This is a matter of a few minutes and you will be able to use this account to build your apps.
  2. Configure QuickBlox SDK for your app. Check out our Setup page for more details.
  3. Create a user session to be able to use QuickBlox functionality. See our Authentication page to learn how to do it.
  4. Connect to the Chat server. See our Connection page to learn how to do it.
  5. Create a dialog. See our Dialogs page to learn how to do it.

Visit our Key Concepts page to get an overall understanding of the most important QuickBlox concepts.

Subscribe message events

Subscribe message events using the subscribeChatEvent() method. As a result, the app will receive events associated with receiving a message, delivery receipts, and read receipts.

//Chat events
//QBChatEvents.RECEIVED_NEW_MESSAGE
//QBChatEvents.RECEIVED_SYSTEM_MESSAGE
//QBChatEvents.MESSAGE_DELIVERED
//QBChatEvents.MESSAGE_READ

StreamSubscription? _someSubscription;

...

@override
void dispose() {
  if(_someSubscription != null) {
    _someSubscription!.cancel();
    _someSubscription = null;
  }
}

...

String event = QBChatEvents.RECEIVED_NEW_MESSAGE;

try {
  _someSubscription = await QB.chat.subscribeChatEvent(event, (data) {
		Map<dynamic, dynamic> map = Map<dynamic, dynamic>.from(data);
    Map<dynamic, dynamic> payload = Map<dynamic, dynamic>.from(map["payload"]);
    String? messageId = payload["id"];
  }
});
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

Send text message

To send a message to a private dialog, use the code snippet below.

String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba";
String body = "test body";
List<QBAttachment>? attachments = [];
Map<String, String>? properties = Map();
bool markable = false;
String dateSent = "2000-023T01:23:45.678+09:00";
bool saveToHistory = true;

try {
  await QB.chat.sendMessage(dialogId, body: body, attachments: attachments, properties: properties, markable: markable, dateSent: dateSent, saveToHistory: saveToHistory);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}
ArgumentRequiredDescription
dialogIdyesThe ID of a dialog.
messageBodynoA message text.
attachmentsnoA list of attachments.
propertiesnoA list of properties.
markablenoA boolean parameter. A markable status of a message.
dateSentnoThe date the message was sent.
saveToHistorynoA boolean parameter. Specifies if the message will be saved on the server. Set the saveToHistory as true to save the message on the server.

📘

You need to join the public and group dialog by calling the join() method before you start chatting in a dialog. Once the dialog is joined, you can receive/send messages. See this section to learn how to join the dialog.

To send messages to a group/public dialog, use the following code snippet below.

String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba";
String body = "test body";
bool saveToHistory = true;

try {
  await QB.chat.sendMessage(dialogId, body: body, saveToHistory: saveToHistory);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

Use the same code snippet to send/receive messages for private, group, and public dialog.

Send message with attachment

Chat attachments are supported by the content API. In order to send a chat attachment, you need to upload the file to QuickBlox cloud storage and obtain a link to the file (file UID). Then you need to include this UID into chat message and send it.

String url = "https://picutres.com/9384erw343.jpg";
bool public = false;

try {
  QBFile? file = await QB.content.upload(url, public: public);
  
  if(file != null) {
  	int id = file!.id;
  	String contentType = file.contentType;
     
  	QBAttachment attachment = QBAttachment();
  	attachment.id = id.toString();
  	attachment.contentType = contentType;

  	//Required parameter
  	attachment.type = "PHOTO";
    
  	List<QBAttachment> attachmentsList = [];
  	attachmentsList.add(attachment);
     
  	QBMessage message = QBMessage();
  	message.attachments = attachmentsList;
  
  	// Send a message logic
  }
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

The flow on the receiver's side is the following: when you receive a message, you need to get the file URL to download the file from the cloud storage.

String uid = "0we8fjlkj34432n2lfkdj";

try {
  String? url = await QB.content.getPrivateURL(uid);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

Send message with extra data

You have the option to extend the message with additional fields. Specify one or more key-value items to the message. Using these items, you can implement the ability for a user to send self-location information to another user or notification messages signifying that a user has left a group, etc.

String dialogId = "dsfs9344349hjkdsda9877932j2";
String body: 'How are you today!';
  
bool saveToHistory = true;
  
Map<String, String> properties = Map();
properties["customParam1"] = "book";
properties["customParam2"] = "21";

try {
  await QB.chat.sendMessage(dialogId, body: body, saveToHistory: saveToHistory, properties: properties);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}
ArgumentRequiredDescription
dialogIdyesThe ID of a dialog.
bodynoA message text.
saveToHistorynoA boolean parameter. Specifies if the message will be saved on the server. Set the saveToHistory as true to save the message on the server.
propertiesnoExtra data. Specify any key-value pairs. In each pair, the key and value are both string values.

Retrieve message history

Every dialog stores its chat history that you can retrieve using the getDialogMessages() method. The request below will return messages for a specific dialog containing test value in its name, sorted by the QBChatDialogSorts.LAST_MESSAGE_DATE_SENT field in ascending order, limited to 50 messages on the page.

String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba";

QBSort sort = QBSort();
sort.field = QBChatMessageSorts.DATE_SENT;
sort.ascending = false;

QBFilter filter = QBFilter();
filter.field = QBChatMessageFilterFields.ID;
filter.value = messageID;
filter.operator = QBChatMessageFilterOperators.IN;

int limit = 100;
int skip = 50;
bool markAsRead = true;

try {
  List<QBMessage?> messages = await QB.chat.getDialogMessages(dialogId, sort: sort, filter: filter, limit: limit, skip: skip, markAsRead: markAsRead);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}
ArgumentRequiredDescription
dialogIdyesThe ID of the dialog.
sortnoSpecifies sorting criteria for the field.
filternoSpecifies filtering criteria for the field.
limitnoLimit search results to N records. Useful for pagination. Default value: 100.
skipnoSkip N records in search results. Useful for pagination. Default (if not specified): 0.
markAsReadnoA boolean parameter.

If you want to mark all retrieved chat messages as a read, set the markAsRead parameter as true. If you decide not to mark chat messages as read, just set markAsRead parameter as false or omit the parameter.

If you want to retrieve only messages updated after some specific date time and order the search results, you can apply operators. This is useful if you cache messages somehow and do not want to obtain the whole list of messages on every app start. Thus, you can apply search and sort operators to list messages on the page so that it is easier to view specific messages. The operators are set as key-value parameters in the extendedRequest dictionary.

Search operators

You can use search operators to search for the exact data that you need.

Search operatorsApplicable to typesApplicable to fieldsDescription
ltNumber, String, Datedate_sent, sender_id, recipient_id, updated_atLess Than operator.
lteNumber, String, Datedate_sent, sender_id, recipient_id, updated_atLess Than or Equal to operator.
gtNumber, String, Datedate_sent, sender_id, recipient_id, updated_atGreater Than operator.
gteNumber, String, Datedate_sent, sender_id, recipient_id, updated_atGreater Than or Equal to operator.
neNumber, String, Date_id, message, date_sent, sender_id, recipient_idNot Equal to operator.
inNumber, String, Datedate_sent, sender_id, recipient_idIN array operator.
ninNumber, String, Datedate_sent, sender_id, recipient_idNot IN array operator.
orNumber, String, Datedate_sent, sender_id, recipient_idAll records that contain a value 1 or value 2.
ctnStringmessageAll records that contain a particular substring.

Sort operators

Here are the sort options that you can use to order the search results.

Sort optionsApplicable to typesDescription
ascendingAll typesSort results in the ascending order by setting the ascending as true.
descendingAll typesSort results in the descending order by setting the ascending as false.

Check if a message is sent

The message is considered as sent if it has been delivered to the server. To get to know that a message has been delivered to the server, make sure to enable a stream management before connecting to the Chat server. See this section to learn how to enable the stream management.

Thus, you send a message to the server and if no error is returned, it is considered as sent (by default). There is no field for a sent status in the message model.

String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba";
String body = "hello";

try {
  await QB.chat.sendMessage(dialogId, body: body);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details    
}

🚧

You should enable Stream Management before you do the login() because the Stream Management is initialized while Chat login is performed.

The Stream Management defines an extension for active management of a stream between a client and server, including features for stanza acknowledgments.

Mark message as delivered

As a sender, you may want to be informed that a message has been successfully delivered to the recipient. The mark-as-delivered functionality allows to notify the sender about message delivery.

To track the event when the message has been delivered to the user, you need to subscribe to this event. As a result, when a user receives a message, the SDK receives the QBChatEvents.MESSAGE_DELIVERED event.

StreamSubscription? _deliveredMessageSubscription;

...

@override
void dispose() {
  if(_deliveredMessageSubscription != null) {
  	_deliveredMessageSubscription!.cancel();
		_deliveredMessageSubscription = null;
  }
}

...

try {
  _deliveredMessageSubscription = await QB.chat.subscribeChatEvent(QBChatEvents.MESSAGE_DELIVERED, (data) {
    LinkedHashMap<dynamic, dynamic> messageStatusHashMap = data;
    Map<dynamic, dynamic> messageStatusMap = Map<dynamic, dynamic>.from(messageStatusHashMap);
    Map<dynamic, dynamic> payloadMap = Map<String,Object>.from(messageStatusHashMap["payload"]);

  String messageId = payloadMap["messageId"];
  String userId = payloadMap["userId"];
  String statusType = messageStatusMap["type"];
	}
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

Use the markMessageDelivered() method to mark a message as delivered. As a result, the server will notify a sender about the delivery receipt.

QBMessage message = new QBMessage();
message.dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba";
message.id = "dslsldf8873lkl&&KjJKJ8fe";
message.senderId = 89987878;

try {
  await QB.chat.markMessageDelivered(message);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

A message can be marked as delivered automatically by the server once a message is successfully delivered to the recipient. Set the markable as true using the sendMessage() method if you want, as a sender, to receive message delivery receipts from other recipients. Thus, the markable parameter enables the sender to request the delivery receipt. It also enables the recipient to confirm the message delivery. However, if markable is false or omitted, then you can notify a sender about the delivery receipt using the markMessageDelivered() method.

String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba";
String body = "test body";
List<QBAttachment>? attachments = [];
Map<String, String>? properties = Map();
bool markable = false;
String dateSent = "2000-023T01:23:45.678+09:00";
bool saveToHistory = true;

try {
  await QB.chat.sendMessage(dialogId, body: body, attachments: attachments, properties: properties, markable: markable, dateSent: dateSent, saveToHistory: saveToHistory);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

📘

Make sure to understand, that marking-as-delivered operation just confirms the fact of message delivery. The message acquires the delivered status when the QBChatEvents.MESSAGE_DELIVERED event is received.

When a message is marked as delivered, the IDs of users who have received the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who received the message using the getDialogMessages() method. See this section to learn how to retrieve chat history.

Mark message as read

As a sender, you may want to be informed that a message has been read by the recipient. The mark-as-read functionality allows to notify the sender that a message has been read.

To track the event when the message has been read by the user, you need to subscribe to this event using the code snippet below. As a result, when a user reads a message, the SDK receives the QBChatEvents.MESSAGE_READ event.

StreamSubscription? _readMessageSubscription;

...

@override
void dispose() {
  if(_readMessageSubscription != null) {
  	_readMessageSubscription!.cancel();
    _readMessageSubscription = null;
  }
}

...

try {
  _readMessageSubscription = await QB.chat.subscribeChatEvent(QBChatEvents.MESSAGE_READ, (data) {
    LinkedHashMap<dynamic, dynamic> messageStatusHashMap = data;
    Map<dynamic, dynamic> messageStatusMap = Map<dynamic, dynamic>.from(messageStatusHashMap);
    Map<dynamic, dynamic> payloadMap = Map<dynamic, dynamic>.from(messageStatusHashMap["payload"]);

    String messageId = payloadMap["messageId"];
    String userId = payloadMap["userId"];
    String statusType = messageStatusMap["type"];
  }
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

Use the markMessageRead() method to mark a message as read. As a result, the server will notify a sender about the read receipt.

QBMessage message = new QBMessage();
message.dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba";
message.id = "dslsldf8873lkl&&KjJKJ8fe";
message.senderId = 89987878;

try {
  await QB.chat.markMessageRead(message);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

📘

When a message is marked as read, the IDs of users who have read the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who read the message using the getDialogMessages() method. See this section to learn how to retrieve chat history.

Send typing indicators

You may want, as a sender, to let the recipient know that you are typing the message or have stopped typing the message. Use typing indicators as a form of chat-specific presence. Typing indicators allow to indicate if users are typing messages in a dialog at the moment.

There are the following typing notifications supported.

  • typing. The user is composing a message. The user is actively interacting with a message input interface specific to this chat session (e.g. by typing in the input area of a chat window).
  • stopped. The user had been composing but now has stopped. The user has been composing but has not interacted with the message input interface for a short period of time (e.g. 30 seconds).

To track the event when the sender is typing the message, you need to subscribe to this event. As a result, when a sender is typing a message, the SDK receives the QBChatEvents.USER_IS_TYPING event.

StreamSubscription? _userTypingSubscription;

...

@override
void dispose() {
  if(_userTypingSubscription != null){
  	_userTypingSubscription!.cancel();
    _userTypingSubscription = null;
  }
}

...

try {
  _userTypingSubscription = await QB.chat.subscribeChatEvent(QBChatEvents.USER_IS_TYPING, (data) {
    Map<dynamic, dynamic> map = Map<dynamic, dynamic>.from(data);
    Map<dynamic, dynamic> payload = Map<dynamic, dynamic>.from(map["payload"]);
    int userId = payload["userId"];
  }
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

To track the event when the sender has stopped typing, you need to subscribe to this event. Thus, when a sender has stopped typing a message, the SDK receives the QBChatEvents.USER_STOPPED_TYPING event.

StreamSubscription? _userStopTypingSubscription;

...

@override
void dispose() {
  if(_userStopTypingSubscription != null){
  	_userStopTypingSubscription!.cancel();
    _userStopTypingSubscription = null;
  }
}

...

try {
  _userStopTypingSubscription = await QB.chat.subscribeChatEvent(QBChatEvents.USER_STOPPED_TYPING, (data) {
    Map<dynamic, dynamic> map = Map<dynamic, dynamic>.from(data);
    Map<dynamic, dynamic> payload = Map<dynamic, dynamic>.from(map["payload"]);
    int userId = payload["userId"];
  }
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

To notify a recipient that a sender is typing the message, use the sendIsTyping() method. As a result, the server will notify a recipient about the event.

String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba";

try {
  await QB.chat.sendIsTyping(dialogId);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

To notify a recipient that a sender had been composing a message but now has stopped, use the sendStoppedTyping() method. As a result, the server will notify a recipient about the event.

String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba";

try {
  await QB.chat.sendStoppedTyping(dialogId);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}

Send system messages

There is a way to send system messages to other users about some events. For example, a system message can be sent when a user has joined or left a group dialog. These messages work over a separate channel and are not be mixed up with regular chat messages. Thus, in order to receive these messages, you should subscribe to the QBChatEvents.RECEIVED_SYSTEM_MESSAGE event. See this section to learn how to subscribe to message events.

System messages are also not shown in the dialog history and, consequently, are not stored on the server. This means that these messages will be delivered only to online users. Send system messages using the sendSystemMessage() method.

int recipientId = 109364799;
Map<String, String> properties = Map();
properties["someKey"] = "someValue";

try {
  await QB.chat.sendSystemMessage(recipientId, properties: properties);
} on PlatformException catch (e) {
  // Some error occurred, look at the exception message for more details
}
ArgumentRequiredDescription
recipientIdyesID of the recipient.
propertiesnoExtra data. Specify any key-value pairs. In each pair, the key and value are both string values.

What’s Next