---
url: https://talkjs.com/docs/Data_APIs/Flutter/Messages
title: 'Messages'
minidoc-source: dart
---

A message contains some content that was sent in a conversation.
Users send messages using [ConversationRef.send](/Data_APIs/Flutter/Conversations/#ConversationRef__send).
You can track a conversation's messages in real-time using [ConversationRef.subscribeMessages](/Data_APIs/Flutter/Conversations/#ConversationRef__subscribeMessages).

## MessageRef
/** References the message with a given message ID.
Used in all Data API operations affecting that message, such as fetching or editing the message attributes, or deleting the message. Created via [ConversationRef.message] and [ConversationRef.send]. */
class MessageRef {
/** The ID of the referenced message.
Immutable: if you want to reference a different message, get a new MessageRef instead. */
final String id;

/** The ID of the conversation that the referenced message belongs to.
Immutable: if you want to reference a message from a different conversation, get a new MessageRef from that conversation. */
final String conversationId;

/** Fetches a snapshot of the message.
@return A snapshot of the message's attributes, or null if the message doesn't exist, the conversation doesn't exist, or you're not a participant in the conversation. */
Future<MessageSnapshot?>get()

/** Edits this message.
Properties that are `null` will not be changed. To clear / reset a property to the default, call [MessageRef.deleteFields] instead.
The promise will reject if the request is invalid, the message doesn't exist, or you do not have permission to edit that message.
@param text The new text to set as the message body
@param custom Custom metadata you have set on the user. This value acts as a patch. Remove specific properties by calling [MessageRef.deleteFields] Default = no custom metadata */
Future<void>edit({String? text, Map<String, String?>? custom})

/** Edits this message.
Properties that are `null` will not be changed. To clear / reset a property to the default, call [MessageRef.deleteFields] instead.
This is the more advanced method for editing a message. It gives you full control over the message content. You can decide exactly how a text message should be formatted, edit an attachment, or even turn a text message into a location.
@param content The new content for the message.
@param custom Custom metadata you have set on the message. This value acts as a patch. Remove specific properties by calling [MessageRef.deleteFields] Default = no custom metadata */
Future<void>editMessage({required List<ContentBlock>content, Map<String, String?>? custom})

/** Deletes properties of this message.
To delete a field in the `custom` property, pass it as `custom.FIELD_TO_DELETE`.
@param fields The names of the properties to delete */
Future<void>deleteFields(List<String>fields)

/** Deletes this message, or does nothing if the message does not exist.
Deleting a nonexistent message is treated as success.
This function will throw if you are not a participant in the conversation or if your role does not give you permission to delete this message. */
Future<void>delete()

/** Get a reference to a specific emoji reaction on this message
If you call `.reaction` with an invalid emoji, it will still succeed and you will still get a [ReactionRef]. However, the TalkJS server will reject any calls that use an invalid emoji.
In the future, this will also be used to fetch a full list of people who used that specific reaction on the message.
Example 1:
Reacting to the message with a Unicode emoji
```dart
final reaction = await messageRef.reaction("🚀");
await reaction.add();
```
Example 2:
Removing your custom emoji reaction from the message
```dart
final reaction = await messageRef.reaction(":cat-roomba:");
await reaction.remove();
```
@param emoji The emoji for the reaction you want to reference. a single Unicode emoji like "🚀" or a custom emoji like ":cat_roomba:". Custom emoji can be up to 50 characters long.
@return A [ReactionRef] for the reaction with that emoji on this message. Throws If the emoji is not a string or is an empty string */
Future<ReactionRef>reaction(String emoji)
}

## MessageSnapshot
/** A snapshot of a message in a conversation.
A snapshot is a frozen view of the message's attributes at the time it was fetched. If the message is edited after the snapshot is taken, the snapshot will not automatically update. */
class MessageSnapshot {
/** The unique ID that is used to identify the message in TalkJS */
final String id;

/** Whether this message was "from a user" or a general system message without a specific sender.
The `sender` property is always present for `USER_MESSAGE` messages and never present for `SYSTEM_MESSAGE` messages. */
final MessageType type;

/** A snapshot of the user who sent the message, or null if it is a system message. The user's attributes may have been updated since they sent the message, in which case this snapshot contains the updated data. It is not a historical snapshot. */
UserSnapshot? sender;

/** Custom metadata you have set on the message */
final Map<String, String>custom;

/** Time at which the message was sent, as a unix timestamp in milliseconds. */
final int createdAt;

/** Time at which the message was last edited, as a unix timestamp in milliseconds. `null` if the message has never been edited. */
int? editedAt;

/** A snapshot of the message that this message is a reply to, or `null` if this message is not a reply.
Only UserMessages can reference other messages. The referenced message snapshot does not have a `referencedMessage` field. Instead, it has `referencedMessageId`. This prevents TalkJS fetching an unlimited number of messages in a long chain of replies. */
ReferencedMessageSnapshot? referencedMessage;

/** Where this message originated from:
- `WEB` = Message sent via the UI or via `ConversationBuilder.sendMessage` - `REST` = Message sent via the REST API's "send message" endpoint - `IMPORT` = Message sent via the REST API's "import messages" endpoint - `EMAIL` = Message sent by replying to an email notification */
final MessageOrigin origin;

/** The contents of the message, as a plain text string without any formatting or attachments. Useful for showing in a conversation list or in notifications. */
final String plaintext;

/** The main body of the message, as a list of blocks that are rendered top-to-bottom. */
final List<ContentBlock>content;

/** All the emoji reactions that have been added to this message.
There can be up to 50 different reactions on each message. */
final List<ReactionSnapshot>reactions;

MessageSnapshot(Map<String, dynamic>json)

Map<String, dynamic>toJson()
}

## ReferencedMessageSnapshot
/** A snapshot of a message that another message is replying to.
Unlike [MessageSnapshot], this type does not expand the referenced message further. Instead of a nested [MessageSnapshot], it only contains the [referencedMessageId], preventing TalkJS from fetching an unlimited number of messages in a long chain of replies. */
class ReferencedMessageSnapshot {
/** The unique ID that is used to identify the message in TalkJS */
final String id;

/** Referenced messages are always `USER_MESSAGE` because you cannot reply to a system message. */
final MessageType type;

/** A snapshot of the user who sent the message. The user's attributes may have been updated since they sent the message, in which case this snapshot contains the updated data. It is not a historical snapshot.
Guaranteed to be set, unlike in MessageSnapshot, because you cannot reference a SystemMessage */
UserSnapshot? sender;

/** Custom metadata you have set on the message */
final Map<String, String>custom;

/** Time at which the message was sent, as a unix timestamp in milliseconds */
final int createdAt;

/** Time at which the message was last edited, as a unix timestamp in milliseconds. `null` if the message has never been edited. */
int? editedAt;

/** The ID of the message that this message is a reply to, or null if this message is not a reply.
Since this is a snapshot of a referenced message, we do not automatically expand its referenced message. The ID of its referenced message is provided here instead. */
String? referencedMessageId;

/** Where this message originated from:
- `WEB` = Message sent via the UI or via `ConversationBuilder.sendMessage`
- `REST` = Message sent via the REST API's "send message" endpoint
- `IMPORT` = Message sent via the REST API's "import messages" endpoint
- `EMAIL` = Message sent by replying to an email notification */
final MessageOrigin origin;

/** The contents of the message, as a plain text string without any formatting or attachments. Useful for showing in a conversation list or in notifications. */
final String plaintext;

/** The main body of the message, as a list of blocks that are rendered top-to-bottom. */
final List<ContentBlock>content;

/** All the emoji reactions that have been added to this message. */
final List<ReactionSnapshot>reactions;

ReferencedMessageSnapshot(Map<String, dynamic>json)

Map<String, dynamic>toJson()
}

## MessageSubscription
/** A subscription to the messages in a specific conversation.
Get a MessageSubscription by calling [ConversationRef.subscribeMessages]
The subscription is 'windowed'. It includes all messages since a certain point in time. By default, you subscribe to the 30 most recent messages, and any new messages that are sent after you subscribe.
You can expand this window by calling [MessageSubscription.loadMore], which extends the window further into the past.
Remember to `.unsubscribe` the subscription once you are done with it. */
class MessageSubscription {
/** Resolves when the subscription starts receiving updates from the server.
Wait for this promise if you want to perform some action as soon as the subscription is active.
The promise rejects if the subscription is terminated before it connects. */
final Future<void>connected;

/** Resolves when the subscription permanently stops receiving updates from the server.
This is either because you unsubscribed or because the subscription encountered an unrecoverable error. */
final Future<void>terminated;

/** Expand the window to include older messages
Calling `loadMore` multiple times in parallel will still only load one page of messages.
@param count The number of additional messages to load. Must be between 1 and 100 */
Future<void>loadMore(int? count)

/** Unsubscribe from this resource and stop receiving updates.
If the subscription is already in the [UnsubscribedState] or [ErrorState], this is a no-op. */
Future<void>unsubscribe()
}

## MessageType
/**<p>Values:</p><ul><li>UserMessage</li><li>SystemMessage</li></ul>*/
enum MessageType {
UserMessage,
SystemMessage
}

## MessageOrigin
/**<p>Values:</p><ul><li>web</li><li>rest</li><li>import</li><li>email</li></ul>*/
enum MessageOrigin {
web,
rest,
import,
email
}

## ReactionRef
/** References a specific emoji reaction on a message.
Used in all Data API operations affecting that emoji reaction, such as adding or removing the reaction. Created via [MessageRef.reaction]. */
class ReactionRef {
/** Which emoji the reaction is using.
Either a single Unicode emoji, or the name of a custom emoji with a colon at the start and end. This is not validated until you send a request to the server. Since custom emoji are configured in the frontend, there are no checks to make sure a custom emoji actually exists.
Immutable: if you want to use a different emoji, get a new ReactionRef instead.
Example 1:
Unicode emoji
"👍"
Example 2:
Custom emoji
":cat-roomba:" */
final String emoji;

/** The ID of the message that this is a reaction to.
Immutable: if you want to react to a different message, get a new ReactionRef instead. */
final String messageId;

/** The ID of the conversation the message belongs to.
Immutable: if you want to reference a message from a different conversation, get a new MessageRef from that conversation and call `.reaction` on that MessageRef. */
final String conversationId;

/** Adds this emoji reaction onto the message, from the current user.
The promise will reject if the request is invalid, the message doesn't exist, there are already 50 different reactions on this message, or if you do not have permission to use emoji reactions on that message. */
Future<void>add()

/** Removes this emoji reaction from the message, from the current user.
The promise will reject if the request is invalid, the message doesn't exist, or you do not have permission to use emoji reactions on that message. */
Future<void>remove()
}

## ReactionSnapshot
/** A summary of a single emoji reaction on a message. */
class ReactionSnapshot {
/** Which emoji the users reacted with.
Either a single Unicode emoji, or the name of a custom emoji with a colon at the start and end. Since custom emoji are defined in the frontend, they are not validated by the TalkJS server. The UI should ignore reactions that use unrecognised custom emoji.
NOTE: In unicode, it is possible to have multiple emoji that look identical but are represented differently. For example, `"👍" != "👍️"` because the second emoji includes a [variation selector 16 codepoint](https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)). This codepoint forces the character to appear as an emoji.
TalkJS normalises all emoji reactions to be "fully qualified" [according to this list](https://unicode.org/Public/emoji/16.0/emoji-test.txt). This prevents a message having multiple separate 👍 reactions.
Be careful when processing the `emoji` property, as this normalisation might break equality checks:
```dart
// Emoji has unnecessary variation selector 16
final sent = '👍';
// React with thumbs up,
final reaction = await messageRef.reaction(sent);
await reaction.add();
// Fetch the reaction
final snapshot = await messageRef.get();
final received = snapshot!.reactions[0].emoji;
// Fails because TalkJS removed the variation selector
assert(sent == received);
```
Example 1:
Unicode emoji
"👍"
Example 2:
Custom emoji
":cat-roomba:" */
final String emoji;

/** The number of times this emoji has been added to the message. */
final int count;

/** Whether the current user has reacted to the message with this emoji. */
final bool currentUserReacted;

ReactionSnapshot(Map<String, dynamic>json)

Map<String, dynamic>toJson()
}