Home
/
Chat
/
Flutter
/
Group channel

Group channel: Advanced

This page explains the advanced features for group channels. Some of them are the premium features available only to a paying user.


Send typing indicators to other members

If the startTyping() and endTyping() methods are called while the current user is typing a message in a group channel, the onTypingStatusUpdated() in the channel event handler will be invoked on all channel members' devices except the one that belongs to the current user.

Light Color Skin
Copy
groupChannel.startTyping();
groupChannel.endTyping();

class MyClass with ChannelEventHandler {
    
    void onTypingStatusUpdate(GroupChannel channel) {
        // You can get members who are typing.
    }
}

Mark messages as read

To keep the most up-to-date and accurate read status of messages for all group channel members, the markAsRead() method should be called every time one of the members reads messages by entering the channel from a channel list or bringing the opened channel view to the foreground.

If the current user opens a channel and the markAsReadAll() is called, Sendbird server will update both the unread message count of the individual channel and the total unread message count of all the group channels joined by the user. Then the server triggers the onReadReceiptUpdated() method of the channel event handler to notify the change of read status to all channel members' devices, except the one that is being used by the current user.

Note: When a channel member sends a message to the channel, Sendbird server updates the member's read receipt to the time when the message has been sent. Meanwhile, the read receipts of other channel members can be updated when the markAsRead() method is called. If a new member joins the channel, the method works differently based on the value of the display_past_message property of your Sendbird application. If the property is set to YES, the new member’s read receipt will be updated to the sent time of the last message in the channel. If NO, it will be updated to 0.

Light Color Skin
Copy
class MyClass with ChannelEventHandler {
    // Add this class through sendbird.addChannelEventHandler(UNIQUE_HANDLER_ID, this).
    // Or remove it through sendbird.removeChannelEventHandler(UNIQUE_HANDLER_ID)
    // when it gets disposed or no longer needs

    
    void onReadReceiptUpdated(GroupChannel channel) {
        // Read receipt is updated.
    }
}

Note: The display_past_message property determines whether to display past messages to newly joined members when they enter the channel. This property is also linked to the Chat history option, which can be adjusted in the Sendbird Dashboard under Settings > Chat > Channels > Group channels.


Retrieve number of members who haven’t received a message

You can retrieve the number of members who haven’t received a specific message in a group channel through the getUndeliveredMembers() method. If zero is returned, it means that the message has been delivered to all members.

Light Color Skin
Copy
final count = channel.getUndeliveredMembers(message).length;

Retrieve members who have read a message

Using the getReadMember() method, you can view members who have read a specific message in a group channel. The method returns a list of channel members who have read the message by comparing the message’s creation time and the channel members’ read receipt. The list will exclude the current user and the message sender.

Note: Read receipt indicates the timestamp of the latest time when each user has read messages in the channel, in Unix milliseconds.

If you want to keep track of who has read a new message, we recommend to use the getReadMembers() method in the onReadReceiptUpdated() of the channel event handler. Then the client app receivesa a callback from Sendbird server whenever a channel member reads a message. To do so, you should pass the new message object as an argument to a parameter in the getReadMembers() method through the onReadReceiptUpdated().

Light Color Skin
Copy
final readMembers = groupChannel.getReadMembers(baseMessage)

Note: Using the getUnreadMembers() method, you can also view members who have not read a specific message in a group channel, except the current user and the message sender. In the meantime, you can get information on each channel member's read receipt through the getReadStatus() method.


Retrieve number of members who have't read a message

Using the getUnreadMembers() method, you can get the number of members who have not read a specific message in a group channel. To get the exact value, the channel should be updated first through the markAsRead() method before calling the getUnreadMembers().

Light Color Skin
Copy
// Call the 'markAsRead' when the current user views unread messages in a group channel.
groupChannel.markAsRead();

// To listen to an update from all the other channel members' client apps, implement the onReadReceiptUpdated with actions to do when notified.
class MyClass with ChannelEventHandler {
    // Add this class through sendbird.addChannelEventHandler(UNIQUE_HANDLER_ID, this).
    // Or remove it through sendbird.removeChannelEventHandler(UNIQUE_HANDLER_ID)
    // when it gets disposed or no longer needs

    
    void onReadReceiptUpdated(GroupChannel channel) {
        if (currentGroupChannel.channelUrl == channel.channelUrl) {
            // When there are unread messages in the channel,
            messages.forEach((m) {
                final unreadCount = channel.getUnreadMembers(m);
                if (unreadCount <= 0) {
                    // 0 means that all members have read this message.
                } else {
                    // Some of the members haven't read this message.
                }
            });
        }
    }
}

Retrieve the last message of a channel

You can retrieve the last message of a group channel.

Light Color Skin
Copy
final lastMessage = groupChannel.lastMessage;

Retrieve number of unread messages in a channel

You can retrieve the total number of the current user's unread messages in a group channel.

Light Color Skin
Copy
final unreadMessageCount = groupChannel.unreadMessageCount;

Retrieve number of unread messages in all channels

You can retrieve the total number of the current user's unread messages in all joined group channels.

Light Color Skin
Copy
final unreadMessageCount = await sendbird.getTotalUnreadMessageCount();

Retrieve number of channels with unread messages

You can retrieve the total number of the current user's joined group channels with one or more unread messages.

Light Color Skin
Copy
final unreadChannelCount = await sendbird.getTotalUnreadChannelCount();

Send an admin message

If you are using the Custom Plan, you can send admin messages to a group channel using the Sendbird Dashboard or Chat Platform API. To send an admin message through your dashboard, go to the Chat > Group channels, select a group channel, find the message box below, click the Admin message tab, and then write your message in the box. An admin message is limited to 1,000 characters.

Unlike other types of messages, a push notification for an admin message is not available by default. If you want assistance on this, contact our sales team.

Note: For clients using the Free Plan, a suggesting message to upgrade to the Custom Plan will be returned when attempting to send an admin message using the Chat Platform API.


Add extra data to a message

You have the option to create further actions in a channel by using extra data in a message. You can add one or more key-values items to a message which you can save, update, or remove, when necessary. Based on those items, you can design and implement several different actions such as measuring user engagement within a chosen time limit through polls or counting how many times a message has been copied by members.

Note: For the quality of performance, every Sendbird application has its own limits to how many key-values items you can add to a single message, as well as the maximum number of values an item can have. If you would like more information on these limits, contact our sales team.

Light Color Skin
Copy
// When a message has been successfully sent to a channel, items with keys are created.
try {
    final message = await groupChannel.addMessageMetaArray(
        MESSAGE,
        [MessageMetaArray(key: 'referees', values: ['John', 'Brandon', 'Harry', 'Jay']),
        MessageMetaArray(key: 'games', values: ['soccer', 'baseball', 'basketball'])]
    );
} catch (e) {
    // Handle error.
}

// Remove existing values of a specific item by its key.
try {
    final message = await groupChannel.removeMessageMetaArrayKeys(
        MESSAGE,
        [MessageMetaArray(key:'referees', values: ['Brandon', 'Jay'])]
    );
} catch (e) {
    // Handle error.
}

// Delete items by their keys.
try {
    final message = await groupChannel.deleteMessageMetaArrayKeys(
        MESSAGE,
        ['referees', 'games']
    );
} catch (e) {
    // Handle error.
}

To get the key-values items of a message, read the message.metaArrays.


Display Open Graph tags in a message

The Chat SDK supports the URL link preview when a message text contains the URL of a web page.

Note: This feature is turned ON by default for Sendbird applications created since July 2020. If this isn't available for your Sendbird application, contact our support team to enable the feature.

URL link preview with OG metadata in chat view.

If a BaseMessage object includes a valid URL of a website, the object can contain OGMetaData, a class that holds the OG metadata such as title, URL, description, and default image of an OG object.

Note: Some websites don’t provide the OG metadata. In that case, even though the OG protocol states them as requirements, all of the four properties can be null.

List of properties

Property nameDescription

title

Type: String
The title of the OG object as it should appear within the graph. The value can be null.

url

Type: String
The canonical URL of the object that can be used as its permanent ID in the graph. The value can be null.

desc

Type: String
The description of the object. The value can be null.

defaultImage

Type: OGImage
An OGImage instance that contains information about the image that this Open Graph points to. The OGImage also holds its own properties such as type, URL, and size. However, the value can be null.

OGImage

A OGImage, the image contained in an OGMetaData object, is a class that holds image-related data for the OGImage object. The OGImage class can also have its own optional structured properties of URL, secure URL, type, width, height, and alt.

Note: Except for width and height, other fields such as URL, secure URL, type, and alt can be null. If the target website doesn’t provide width and height data, the value of those two fields are set to 0.

List of properties

Property nameDescription

url

Type: String
The URL of an image object within the Open Graph. The value can be null.

secureUrl

Type: String
An alternative URL to use if the webpage requires HTTPS. The value can be null.

type

Type: String
A media type or MIME type of this image. The value can be null.

width

Type: int
The number of pixels horizontal. When the value is unavailable, this method returns 0.

height

Type: int
The number of pixels vertical. When the value is unavailable, this method returns 0.

alt

Type: String
The description of what is in the image, not a caption of the image. The alt attribute is designed to provide a fuller context of the OGImage object and help users better understand it when they can’t load or see the image. The value can be null.

How it works

If a user sends a message with a web page URL and the linked web page possesses Open Graph (OG) tags, or OG metadata, Sendbird server parses the message content, extracts the URL in the message, gets the OG metadata from it, and creates an OG metadata object for the message. Then message recipients will get the parsed message with its OG metadata through the onMessageReceived() method in the channel event handler of the SDK. On the other hand, the message sender will do the same through the onMessageUpdated() method.

Displaying an OG metadata object is available for two subtypes of a BaseMessage: UserMessage and AdminMessage. If the content of either a UserMessage or AdminMessage object includes a web page URL containing OG metadata, the onMessageReceived() method returns OGMetaData and OGImage objects.

If Sendbird server doesn’t have cache memory of the OG metadata of the given URL, the BaseMessage.ogMetaData can be null due to the time it takes to fetch the OG metadata from a remote web page. In the meantime, the message text containing the URL will be delivered first to message recipients’ client app through the onMessageReceived() method. When the server completes fetching, the onMessageUpdated() method will be called and the message with its OGMetaData object will be delivered to the recipients’ client app. However, if Sendbird server has already cached the OG metadata of the URL, the BaseMessage.ogMetaData returns the message and its OGMetaData object instantly and the onMessageUpdated() method won’t be called.

The following code demonstrates when sending a UserMessage object with the URL of a web page.

Light Color Skin
Copy
// Send a user message containing the URL of a web page.
try {
    final params = UserMessageParams()
        ..message = 'sendbird.com';
    final preMessage = await groupChannel.sendUserMessage(params, onCompleted:(message, error) {
        // A message has been sent successfully
    });
} catch (e) {
    // Handle error.
}

The following code demonstrates when receiving a UserMessage object containing OG metadata.

Light Color Skin
Copy
// Receive a user message containing OG metadata of the web page through a channel event handler.
class MyClass with ChannelEventHandler {
    // Add this class via sendbird.addChannelEventHandler(UNIQUE_HANDLER_ID, this).
    // and remove with sendbird.removeChannelEventHandler(UNIQUE_HANDLER_ID)
    // when it gets disposed or no longer needs
    
    void onMessageReceived(BaseChannel channel, BaseMessage message) {
        if (message.ogMetaData != null) {
            // You can create and customize the URL link preview by using the Open Graph metadata of the message.
        } else {
            // If the `message.ogMetaData` is `null`, wait until the `onMessageUpdated` method receives a callback from Sendbird server.
        }
    }

    
    void onMessageUpdated(BaseChannel channel, BaseMessage message) {
        if (message.ogMetaData != null) {
            // You can create and customize the URL link preview by using the Open Graph metadata of the message.
        }
    }
}

Categorize channels by custom type

When creating a group channel, you can additionally specify a custom channel type to subclassify group channels. This custom type takes on the form of a String, and can be useful in searching or filtering group channels.

The data and customType properties of a channel object allow you to append information to your channels. While both properties can be used flexibly, common examples for the customType include categorizing group channels into School or Work.

Light Color Skin
Copy
final params = GroupChannelParams()
    ..userIds = ['Jed', 'Young']
    ..name = NAME
    ..customType = CUSTOME_TYPE;

try {
    final channel = await GroupChannel.createChannel(params);
} catch (e) {
    // Handle error.
}

To get a channel's custom type, read the groupChannel.customType.


Categorize messages by custom type

When sending a message, you can additionally specify a custom message type to subclassify messages. This custom type takes on the form of a String, and can be useful in searching or filtering messages.

The data and customType properties of a message object allow you to append information to your messages. While both properties can be used flexibly, common examples for the customType include categorizing message groups into Notes or Contacts.

To embed a custom type into your message, assign a value to the customType under the UserMessageParams or FileMessageParams object. Then, pass the specified object as an argument to the parameter in the sendUserMessage() or sendFileMessage() method.

Light Color Skin
Copy
final params = UserMessageParams()
    ..message = MESSAGE
    ..customType = CUSTOM_TYPE;

try {
    final preMessage = await groupChannel.sendUserMessage(params, onCompleted: (msg, err) {
        // message has been sent successfully.
    });
} catch (e) {
    // Handle error.
}

To get a message's custom type, read the message.customType.


Search channels by name, URL, or other filters

You can search for specific group channels by adding keywords to a GroupChannelListQuery instance. There are three types of keywords: Name, URL and custom type. The code sample below shows the query instance which returns a list of group channels that partially match the specified Name keyword in the names of the channels.

  • GroupChannelListQuery

A GroupChannelListQuery instance provides many types of search filters such as ChannelNameContains and ChannelUrls filters.

Light Color Skin
Copy
final listQuery = GroupChannelListQuery()
    ..channelNameContains = 'Sendbird';

try {
    final channels = await listQuery.loadNext();
    // a list of group channels that partially match 'Sendbird' in their names is returned.
} catch (e) {
    // Handle error.
}

The following shows the query instance which returns a list of group channels that partially match the specified channelUrls keyword in the URLs of the channels.

Light Color Skin
Copy
final listQuery = GroupChannelListQuery()
    ..channelUrls = 'seminar';

try {
    final channels = await listQuery.loadNext();
    // a list of group channels that partially match 'seminar' in their url is returned.
} catch (e) {
    // Handle error.
}

Using the customTypes like the following, you can also search for group channels with a specific custom type.

Light Color Skin
Copy
final listQuery = GroupChannelListQuery()
    ..customTypes = 'movie';

try {
    final channels = await listQuery.loadNext();
    // a list of group channels with a ‘movie’ custom type is returned.
} catch (e) {
    // Handle error.
}

Search messages by keyword

Message search is a feature that retrieves a list of messages that contain a search query or a specified keyword.

Implement the MessageSearchQuery to search messages in your app. The query will retrieve a list of messages that contain a search term and meet the optional parameter values set by its properties.

The query will retrieve a list of matched messages. Calling the builder method again will return the next page of the results.

Light Color Skin
Copy
try {
    final query = MessageSearchQuery();
    final messages = await query.loadNext();
} catch (e) {
    // Handle error.
}

Use the hasNext() method to see if there is a next page.

Light Color Skin
Copy
query.hasNext

Use the isLoading() method to see if the search results are loading.

Light Color Skin
Copy
query.isLoading

List of parameters of MessageSearchQueryBuilder

You can build the query class using the following parameters, which allows you to add various search options.

Parameter nameDescription

keyword

Type: String
Specifies the search term.

channelUrl

Type: String
Specifies the URL of the target channel.

channelCustomType

Type: String
Specifies the custom channel type.

limit

Type: int
Specifies the number of messages to return per page. Acceptable values are 1 to 99, inclusive. (Default: 20)

exactMatch

Type: bool
Determines whether to search for messages that exactly match the search term. If set to false, it will return partial matches that contain the search term. (Default: false)

messageTimestampFrom

Type: int
Restricts the search scope to the messages sent after the specified value in Unix milliseconds format. This includes the messages sent exactly on the timestamp. (Default: 0)

messageTimestampTo

Type: int
Restricts the search scope to the messages sent before the specified value in Unix milliseconds format. This includes the messages sent exactly on the timestamp. (Default: 0)

order

Type: MessageSearchQueryOrder
Determines by which field the results are sorted. Acceptable values are the following:
- MessageSearchQueryOrderScore: a relevance score.
- MessageSearchQueryOrderTimeStamp: the time when a message was created.
(Default: MessageSearchQueryOrderScore)

reverse

Type: bool
Determines whether to sort the results in reverse order. If set to false, they will be sorted in descending order. (Default: false)


Generate thumbnails of a file message

When sending an image file, you can determine whether to create thumbnails of the image, which you can fetch and render into your UI. You can specify up to 3 different dimensions to generate thumbnail images in, which can be useful for supporting various display densities.

Note: Only files whose media type is image/* or video/* are supported. The Chat SDK doesn't support creating thumbnails when sending a file message with a file URL.

The sendFileMessage() method requires passing a FileMessageParams object as an argument to a parameter, containing a list of thumbnailSizes objects which each specify the maximum values of width and height of a thumbnail image. The onCompleted callback subsequently returns a list of Thumbnail objects that each contain the URL of the generated thumbnail image file.

Light Color Skin
Copy
// Sending a file message with a raw file
final params = FileMessageParams()
    ..uploadFile = FileInfo.fromData(
        data: FILE,
        name: FILE_NAME,
        size: FILE_SIZE,
        mimeType: MIME_TYPE)
    ..thumbnailSizes =  [Size(100, 100), Size(40, 40)];
    ..customType = CUSTOM_TYPE
    ..mentionType = MentionType.users // This could be either `MentionType.users` or `MentionType.channel`.
    ..mentionedUserIds = ['Jeff', 'Julia']
    ..pushOption = PushNotificationDeliveryOption.normal;

try {
    final preMessage = await groupChannel.sendFileMessage(params: params, onCompleted: (message, error) {
        If (error != null) {
            // Handle error.
        }
        // A file message with detailed configuration is successfully sent to the channel.
        // By using fileMesage.messageId, fileMessage.fileName, fileMessage.customType, and so on,
        // you can access the result object from the Sendbird server to check your FileMessageParams configuration.
        // The current user can receive messages from other users through the onMessageReceive method of an channel event handler.
    });
} catch (e) {
    // Handle error.
}

A thumbnail image is generated evenly to fit within the bounds of maxWidth and maxHeight, which are provided through the constructor. Note that if the original image is smaller than the specified dimensions, the thumbnail isn't resized. The url returns the location of the generated thumbnail file within Sendbird server.

Note: This is one of Sendbird's premium features. Contact our sales team for further assistance.


Track file upload progress using a handler

If needed, you can track the file upload progress by passing the function as an argument to a parameter when calling the sendFileMessage() method.

Light Color Skin
Copy
try {
    final preMessage = groupChannel.sendFileMessage(
        params,
        progress: (bytesSent, totalBytesSent) {
            // Progress here.
        },
        onCompleted: (msg, err) {
            // Do something with the sent file message.
        });
}

Share an encrypted file with other members

This file encryption feature prevents users without access from opening and reading encrypted files that have been shared within a group of users. When this feature is turned on, all types of sent files and thumbnail images will be first uploaded to Sendbird server, and then encrypted by AES256.

In a group channel, encrypted files and thumbnail images will be decrypted and accessed securely only by the members. Anyone outside of the channel and application will not have access to those files and thumbnail images. The following explains how this data security works and what to do at the SDK level to apply it to your client apps.

The Sendbird system enables secure encryption and decryption of files by generating and distributing an opaque and unique encryption key for each user. An encryption key is managed internally by the system, and is valid for 3 days. It is generated every time the user logs in to Sendbird server through the Chat SDK, which then gets delivered to the Chat SDK from the server.

When the Chat SDK requests an encrypted file by its URL, the parameter auth should be added to the URL to access the file, which is specified with an encryption key of the user such as ?auth=RW5jb2RlIHaXMgdGV4eA==. With the specified key in the auth, Sendbird server first decrypts the file, then checks if the user is a member of the group channel, and finally, allows the user to access and open the file in the channel.

This can be easily done by retrieving the fileMessage.url property, which returns the unique file URL containing the parameter auth with an encryption key of the current user.


Spam flood protection

This feature allows you to customize the number of messages a participant can send in an open channel per second. By doing so, all excess messages from a participant will be deleted and only the number of messages allowed to be sent per participant per second will be delivered. This feature protects your app from some participants spamming others in the channel with the same messages.

Note: Our default system setting is 5 messages per second. This limit can be manually adjusted only from our side. This is one of Sendbird's premium features. Contact our sales team for further assistance.


Message auto-translation

It is possible for text messages to be sent in different languages through the Sendbird's auto-translation feature. When sending a text message, set a list of language codes to a UserMessageParams object and then pass the object as an argument to parameter in the sendUserMessage() method to request translated messages in the corresponding languages.

Note: This message auto-translation feature is powered by Google Cloud Translation API recognition engine and Microsoft Translator engine. It is by default that Google's Cloud Translation API recognition engine is used for message auto-translation, whereas if your Sendbird application has been created before June 26, 2019, Microsoft Translator engine is used.

Light Color Skin
Copy
final params = UserMessageParams()
    ..targetLanguages = ['es', 'ko'];    // Spanish and Korean

try {
    final preMessage = await groupChannel.sendUserMessage(params, onCompleted: (msg, error){
        // A message is successfully sent and translation of the message is accessible through translations property.
    });
} catch (e) {
    // Handle error.
}

You can retrieve translations of a text message using the userMessage.translations property which has a NSArray object containing the language codes and translations.

Note: The two translation engines support a wide variety of languages, and you can see their language code tables in the Miscellaneous > Language support section.

Light Color Skin
Copy
class MyClass with ChannelEventHandler {
    // Add this class via sendbird.addChannelEventHandler(UNIQUE_HANDLER_ID, this).
    // and remove with sendbird.removeChannelEventHandler(UNIQUE_HANDLER_ID)
    // when it gets disposed or no longer needs
    
    void onMessageReceived(BaseChannel channel, BaseMessage message) {
        If (message is UserMessage) {
            final esTranslatedMessage = message.translations[‘es’];     // Spanish
            // Display translation in UI.
        }
    }
}

This is one of Sendbird's premium features. Contact our sales team for further assistance.


Message on-demand translation

Using the translateUserMessage() method, you can allow your users to translate text messages into other languages when needed.

Note: This message on-demand translation is powered by Google Cloud Translation API recognition engine, and you can see its language code table in the Miscellaneous > Language support section.

Light Color Skin
Copy
// The USER_MESSAGE below indicates a UserMessage object which represents an already sent or received text message.
try {
    final message = await groupchannel.translateUserMessage(
        USER_MESSAGE, ['es', 'de']
    );
    final esTranslatedMessage = message.translations['es']      // Spanish
    final deTranslatedMessage = message.translations['de']      // German
} catch (e) {
    // Handle error.
}
// Display translations in UI.

Based on this method, you can implement features such as real-time or instant translation to your app. You can also implement to only translate a selected message into preferred languages.

This is one of Sendbird's premium features. Contact our sales team for further assistance.