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

A conversation is a chat channel where messages can be sent.
Users can participate in many conversations at once.

## ConversationRef
class ConversationRef {
/** The ID of the referenced conversation.
Immutable: if you want to reference a different conversation, get a new `ConversationRef` instead. */
final String id;

/** Fetches a snapshot of the conversation.
This contains all of the information related to the conversation and the current user's participation in the conversation.
@return A snapshot of the current user's view of the conversation, or `null` if the current user is not a participant (including if the conversation doesn't exist) */
Future<ConversationSnapshot?>get()

/** Sets properties of this conversation and your participation in it.
Properties that are `null` will not be changed. To clear / reset a property to the default, call [ConversationRef.deleteFields] instead.
The conversation is created if a conversation with this ID doesn't already exist. You are added as a participant if you are not already a participant in the conversation. When client-side conversation syncing is disabled, you may only set your `notify` property, when you are already a participant. Everything else requires client-side conversation syncing to be enabled, and will cause the function to throw.
@param subject The conversation subject to display in the chat header. Default = no subject, list participant names instead.
@param photoUrl The URL for the conversation photo to display in the chat header. Default = no photo, show a placeholder image.
@param welcomeMessages System messages which are sent at the beginning of a conversation. Default = no messages.
@param custom Custom metadata you have set on the conversation. This value acts as a patch. Remove specific properties by calling [ConversationRef.deleteFields] Default = no custom metadata
@param access Your access to the conversation. Default = `READ_WRITE` access.
@param notify Your notification settings. Default = `TRUE` */
Future<void>set({String? subject, String? photoUrl, List<String>? welcomeMessages, Map<String, String?>? custom, ConversationAccess? access, NotificationSettings? notify})

/** Creates this conversation if it does not already exist.
Properties that are `null` will be set to the default
Adds you as a participant in this conversation, if you are not already a participant.
If the conversation already exists or you are already a participant, this operation is still considered successful. The promise rejects if you are not already a participant and client-side conversation syncing is disabled.
@param subject The conversation subject to display in the chat header. Default = no subject, list participant names instead
@param photoUrl The URL for the conversation photo to display in the chat header. Default = no photo, show a placeholder image.
@param welcomeMessages System messages which are sent at the beginning of a conversation. Default = no messages.
@param custom Custom metadata you have set on the conversation. Default = no custom metadata
@param access Your access to the conversation. Default = `READ_WRITE` access.
@param notify Your notification settings. Default = `TRUE` */
Future<void>createIfNotExists({String? subject, String? photoUrl, List<String>? welcomeMessages, Map<String, String>? custom, ConversationAccess? access, NotificationSettings? notify})

/** Deletes properties of this conversation.
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)

/** Marks the conversation as read.
The promise rejects if you are not a participant in the conversation. */
Future<void>markAsRead()

/** Marks the conversation as unread.
The promise rejects if you are not a participant in the conversation. */
Future<void>markAsUnread()

/** Marks the current user as typing in this conversation for 10 seconds.
This means that other users will see a typing indicator in the UI, from the current user.
The user will automatically stop typing after 10 seconds. You cannot manually mark a user as "not typing". Users are also considered "not typing" when they send a message, even if that message was sent from a different tab or using the REST API.
To keep the typing indicator visible for longer, call this function again to reset the 10s timer. */
Future<void>markAsTyping()

/** Get a reference to a participant in this conversation
@param user the user's ID
@return A reference to the given participant */
Future<ParticipantRef>participant(String user)

/** Get a reference to a message in this conversation
@return A reference to the message with the given ID */
Future<MessageRef>message(String messageId)

/** Sends a message in the conversation
@param text The text to send in the message.
@param custom Custom metadata you have set on the user. Default = no custom metadata
@param referencedMessage The message that you are replying to. Default = not a reply
@return A reference to the newly created message. The promise rejects if you are not a participant with write access in this conversation. */
Future<MessageRef>send(String text, {Map<String, String>? custom, String? referencedMessage})

/** Sends a message in the conversation
Properties that are `null` will be set to the default.
This is the more advanced method for editing a message, giving 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 most important part of the message, either some text, a file attachment, or a location.
@param custom Custom metadata you have set on the user. Default = no custom metadata
@param referencedMessage The message that you are replying to. Default = not a reply
@return A reference to the newly created message. The promise rejects if you are not a participant with write access in this conversation. */
Future<MessageRef>sendMessage(List<ContentBlock>content, {Map<String, String>? custom, String? referencedMessage})

/** Subscribes to the conversation.
Whenever `Subscription.state.type` is "active" and something about the conversation changes, `onSnapshot` will fire and `Subscription.state.latestSnapshot` will be updated. This includes changes to nested data. As an extreme example, `onSnapshot` would be called if `snapshot.lastMessage.referencedMessage.sender.name` changes.
The snapshot is null if you are not a participant in the conversation (including when the conversation doesn't exist) */
ConversationSubscription subscribe(void Function(ConversationSnapshot? snapshot) onSnapshot)

/** Subscribes to the messages in the conversation.
Initially, you will be subscribed to the 10 most recent messages and any new messages. Call `loadMore` to load additional older messages.
Whenever a message is edited, a new message is received, or you load more messages, `onSnapshot` will fire and `Subscription.latestSnapshot` will be updated. `loadedAll` is true when the snapshot contains all the messages in the conversation.
The snapshot is `null` if you are not a participant in the conversation (including when the conversation doesn't exist)
@param onSnapshot function called when the list of messages is updated
@return A subscription to messages. */
MessageSubscription subscribeMessages(void Function(List<MessageSnapshot>? snapshot, bool loadedAll) onSnapshot)

/** Subscribes to the participants in the conversation.
While the subscription is active, `onSnapshot` will be called whenever the participant snapshots change. This includes when someone joins or leaves, when their participant attributes are edited, and when you load more participants. It also includes when nested data changes, such as when `snapshot[0].user.name` changes. `loadedAll` is true when `snapshot` contains all the participants in the conversation, and false if you could load more.
The `snapshot` list is ordered chronologically with the participants who joined most recently at the start. When someone joins the conversation, they will be added to the start of the list.
The snapshot is null if you are not a participant in the conversation (including when the conversation doesn't exist)
Initially, you will be subscribed to the 10 participants who joined most recently, and any new participants. Call `loadMore` to load additional older participants. This will trigger `onSnapshot`.
Remember to call `.unsubscribe` on the subscription once you are done with it. */
ParticipantSubscription subscribeParticipants(void Function(List<ParticipantSnapshot>? snapshot, bool loadedAll) onSnapshot)

/** Subscribes to the typing status of the conversation.
Whenever `Subscription.state.type` is "active" and the typing status changes, `onSnapshot` will fire and `Subscription.state.latestSnapshot` will be updated. This includes changes to nested data, such as when a user who is typing changes their name.
The snapshot is null if you are not a participant in the conversation (including when the conversation doesn't exist)
Note that if there are "many" people typing and another person starts to type, `onSnapshot` will not be called. This is because your existing [ManyTypingSnapshot] is still valid and did not change when the new person started to type. */
TypingSubscription subscribeTyping(void Function(TypingSnapshot? snapshot) onSnapshot)
}

## ConversationSnapshot
/** A snapshot of a conversation and the current user's participation in it.
A snapshot is a frozen view of the conversation's attributes at the time it was fetched. If the conversation is updated after the snapshot is taken, the snapshot will not automatically update. */
class ConversationSnapshot {
/** The ID of the conversation */
final String id;

/** Contains the conversation subject, or `null` if the conversation does not have a subject specified. */
String? subject;

/** Contains the URL of a photo to represent the topic of the conversation or `null` if the conversation does not have a photo specified. */
String? photoUrl;

/** One or more welcome messages that will be rendered at the start of this conversation as system messages.
Welcome messages are rendered in the UI as messages, but they are not real messages. This means they do not appear when you list messages using the REST API or JS/Kotlin Data API, and you cannot reply or react to them. */
final List<String>welcomeMessages;

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

/** The date that the conversation was created, as a unix timestamp in milliseconds. */
final int createdAt;

/** The date that the current user joined the conversation, as a unix timestamp in milliseconds. */
final int joinedAt;

/** The last message sent in this conversation, or `null` if not messages have been sent. */
MessageSnapshot? lastMessage;

/** The number of messages in this conversation that the current user hasn't read. */
final int unreadMessageCount;

/** The most recent date that the current user read the conversation.
This value is updated whenever you read a message in a chat UI, open an email notification, or mark the conversation as read using an API like [ConversationRef.markAsRead].
Any messages sent after this timestamp are unread messages. */
final int readUntil;

/** Everyone in the conversation has read any messages sent on or before this date.
This is the minimum of all the participants' `readUntil` values. Any messages sent on or before this timestamp should show a "read" indicator in the UI.
This value will rarely change in very large conversations. If just one person stops checking their messages, `everyoneReadUntil` will never update. */
final int everyoneReadUntil;

/** Whether the conversation should be considered unread.
This can be true even when `unreadMessageCount` is zero, if the user has manually marked the conversation as unread. */
final bool isUnread;

/** The current user's permission level in this conversation. */
final ConversationAccess access;

/** The current user's notification settings for this conversation.
`FALSE` means no notifications, `TRUE` means notifications for all messages, and `MENTIONS_ONLY` means that the user will only be notified when they are mentioned with an `@`. */
final NotificationSettings notify;

ConversationSnapshot(Map<String, dynamic>json)

Map<String, dynamic>toJson()
}

## ConversationSubscription
/** A subscription to a specific conversation.
Get a ConversationSubscription by calling [ConversationRef.subscribe] */
class ConversationSubscription {
/** Resolves when the subscription starts receiving updates from the server. */
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;

/** 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()
}

## ConversationListSubscription
/** A subscription to your most recently active conversations.
Get a ConversationListSubscription by calling [TalkSession.subscribeConversations].
The subscription is 'windowed'. Initially, this window contains the 20 most recent conversations. Conversations are ordered by last activity. The last activity of a conversation is either `joinedAt` or `lastMessage.createdAt`, whichever is higher.
The window will automatically expand to include any conversations you join, and any old conversations that receive new messages after subscribing.
You can expand this window by calling [ConversationListSubscription.loadMore], which extends the window further into the past.
Remember to `.unsubscribe` the subscription once you are done with it. */
class ConversationListSubscription {
/** 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 conversations
Calling `loadMore` multiple times in parallel will still only load one page of conversations.
Avoid calling `.loadMore` in a loop until you have loaded all conversations. This is usually unnecessary: any time a conversation receives a message, it appears at the start of the list of conversations. If you do need to call loadMore in a loop, make sure you set a small upper bound (e.g. 100) on the number of conversations, where the loop will exit.
@param count The number of additional conversations to load. Must be between 1 and 30. Default 20. */
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()
}

## TypingSnapshot
/** A snapshot of the typing indicators in a conversation at a given moment in time.
Currently when there are 5 or less people typing in the conversation, `many` will be `false` and `users` will contain the users who are currently typing in this conversation. When more than 5 people are typing, `many` will be `true` and `users` will be `null`. This limit may change in the future, which will not be considered a breaking change.
Example 1:
Converting a TypingSnapshot to text
```dart
String formatTyping(TypingSnapshot snapshot) {
if (snapshot.many) {
return 'Several people are typing';
}
final names = snapshot.users!.map((user)=>user.name).toList();
if (names.isEmpty) {
return '';
}
if (names.length == 1) {
return '${names[0]} is typing';
}
if (names.length == 2) {
return '${names.join(' and ')} are typing';
}
// Prefix last name with "and "
names.add('and ${names.removeLast()}');
return '${names.join(', ')} are typing';
}
``` */
class TypingSnapshot {
/** Check this to differentiate between few people are typing (`false`) and many people are typing (`true`).
When `false`, you can see the list of users who are typing in the `users` property. */
final bool many;

/** The users who are currently typing in this conversation.
The list is in chronological order, starting with the users who have been typing the longest. The current user is never contained in the list, only other users. When the `many` property is `true`, this property is `null`. */
List<UserSnapshot>? users;

TypingSnapshot(Map<String, dynamic>json)

Map<String, dynamic>toJson()
}

## TypingSubscription
/** A subscription to the typing status in a specific conversation
Get a TypingSubscription by calling [ConversationRef.subscribeTyping].
When there are "many" people typing, the next update you receive will be once enough people stop typing. Until then, your [TypingSnapshot] is still valid and does not need to changed, so `onSnapshot` will not be called. */
class TypingSubscription {
/** 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;

/** 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()
}

## ConversationAccess
/**<p>Values:</p><ul><li>Read</li><li>ReadWrite</li></ul>*/
enum ConversationAccess {
Read,
ReadWrite
}

## NotificationSettings
/**<p>Values:</p><ul><li>yes</li><li>no</li><li>mentionsOnly</li></ul>*/
enum NotificationSettings {
yes,
no,
mentionsOnly
}