# Topic (/zh-CN/api-reference/rtm/flutter/topic)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

Topic 是 Stream Channel 中的数据流管理机制。用户可以在 Stream Channel 中利用此特性进行数据流的订阅、分发、事件通知等。

:::info[信息]
Topic 只存在于 Stream Channel。所以用户在使用相关特性之前，需要先创建一个 `StreamChannel` 对象实例。
:::

想要了解 Topic 的更多特性，可以查看：

- [Topic](/zh-CN/realtime-media/rtm/build/channels-and-topics/topics/topic-basic)

## joinTopic [#jointopic]

### 接口描述 [#接口描述]

加入 Topic 的目的在于注册成为该 Topic 的消息发布者之一，从而让用户具备发送消息的能力。是否执行该操作都不影响该用户是否成为该 Topic 的订阅者。

:::info[信息]
- 当前 RTM 支持单客户端在同一个 Stream Channel 中同时加入 8 个 Topic。
- 在执行此操作之前，用户需要首先创建 `StreamChannel` 对象实例，并调用 `join` 方法加入频道，详见[频道](/zh-CN/realtime-media/rtm/build/channels-and-topics/channel-basic)。
:::

成功加入 Topic 后，订阅该 Topic 并添加事件监听的用户会收到 `remoteJoinTopic` 类型的 `topic` 事件通知。详见[事件监听](/zh-CN/api-reference/rtm/flutter/configuration#事件监听)。

### 接口方法 [#接口方法]

你可以通过以下方式调用 `joinTopic` 方法：

```dart
Future<(RtmStatus, JoinTopicResult?)> joinTopic(
    String topic,
    {
        RtmMessageQos qos = RtmMessageQos.unordered,
        RtmMessagePriority priority = RtmMessagePriority.normal,
        String? meta = '',
        bool? syncWithMedia = false
    }
);
```

| 参数 | 类型 | 必填 | 默认值 | 描述 |
| --- | --- | --- | --- | --- |
| `topic` | String | 必填 | - | Topic 名称。 |
| `qos` | `RtmMessageQos` | 选填 | `unordered` | 定义此 Topic 中传输数据是否保序。详见 [`RtmMessageQos`](/zh-CN/api-reference/rtm/flutter/enumv#messageqos)。 |
| `priority` | `RtmMessagePriority` | 选填 | `normal` | 定义此 Topic 中传输数据的优先级（相比于同频道中的其他 Topic）。详见 [`RtmMessagePriority`](/zh-CN/api-reference/rtm/flutter/enumv#messagepriority)。 |
| `meta` | String | 选填 | - | 加入 Topic 时携带的其他辅助信息。 |
| `syncWithMedia` | bool | 选填 | `false` | 此 Topic 中发送的数据是否与共频道的 RTC 音视频数据流实现数据同步（时间戳对齐）。 |

### 基本用法 [#基本用法]

```dart
var (status,response) = await stChannel.joinTopic("myTopic");

if (status.error == true) {
    print(status);
} else {
    print(response);
}
```

### 返回值 [#返回值]

调用此方法，会返回 `Future<(RtmStatus, JoinTopicResult?)>` 类型的元组数据。

- 无论该方法是否调用成功，元组的第一项都会返回一个 `RtmStatus` 类型的数据，其中字段定义如下：

```dart
class RtmStatus {
    bool error;                   // 本次操作是否出错。
    String errorCode;             // 本次操作错误码。
    String operation;             // 本次操作的 API。
    String reason;                // 本次操作错误原因简述。
}
```

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

- 如果该方法调用成功，元组的第二项将返回一个 `JoinTopicResult` 类型的数据，定义如下：

  ```dart
  class JoinTopicResult {
      final String channelName;                           // 频道名称
      final String userId;                                // 当前userId
      final String topic;                                 // Topic 名称
      final String meta;                                  // 附加数据
  }
  ```

## publishTextMessage [#publishtextmessage]

### 接口描述 [#接口描述-1]

`publishTextMessage` 方法用于向 Topic 发送 String 型消息，频道中订阅此 Topic 及此消息发布者的用户会在 100 毫秒内收到此消息。调用 `publishTextMessage` 方法需要用户首先加入 Stream Channel 并且通过调用 `joinTopic` 方法注册成为该 Topic 的消息发布者。

用户发送的消息在传输的过程中会被 TLS 加密，数据链路加密默认开启且无法关闭。用户也可以在出初始化的时候开启端侧加密，以获得更高的数据安全等级，详见[初始配置](/zh-CN/api-reference/rtm/flutter/configuration)。

### 接口方法 [#接口方法-1]

你可以通过以下方式调用 `publishTextMessage` 方法：

```dart
Future<(RtmStatus, PublishTopicMessageResult?)> publishTextMessage(
    String topic,
    String message,
    {
        int sendTs=0,
        String? customType
    }
);
```

| 参数 | 类型 | 必填 | 默认值 | 描述 |
| --- | --- | --- | --- | --- |
| `topic` | String | 必填 | - | Topic 名称。 |
| `message` | String | 必填 | - | 消息负载。 |
| `sendTs` | int | 选填 | `0` | 消息发送时间戳。只有在 `joinTopic` 中设置 `syncWithMedia = true` 时，该参数才有效，SDK 会根据此时间戳与 RTC 音视频数据流进行数据同步。 |
| `customType` | String | 选填 | - | 用户自定义字段。仅支持 String 型。 |

### 基本用法 [#基本用法-1]

```dart
var message = {
    type: "poll",
    question: "Which option is right?",
    answers: {
        A: "Apple",
        B: "Banana",
        C: "Blackberry"
    },
    sender: "Max"
}

var payload = json.encode(message);
var (status,response) =
    await stChannel.publishTextMessage("myTopic", payload, customType:"poll");
if (status.error == true) {
    print(status);
} else {
    print(response);
}
```

### 返回值 [#返回值-1]

调用此方法，会返回 `Future<(RtmStatus, PublishTopicMessageResult?)>` 类型的元组数据。

- 无论该方法是否调用成功，元组的第一项都会返回一个 `RtmStatus` 类型的数据，其中字段定义如下：

```dart
class RtmStatus {
    bool error;                   // 本次操作是否出错。
    String errorCode;             // 本次操作错误码。
    String operation;             // 本次操作的 API。
    String reason;                // 本次操作错误原因简述。
}
```

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

- 如果该方法调用成功，元组的第二项将返回一个 `PublishTopicMessageResult` 类型的数据，定义如下：

  ```dart
  class PublishTopicMessageResult {
      final String channelName;                           // 频道名称
      final String topic;                                 // Topic 名称
  }
  ```

## publishBinaryMessage [#publishbinarymessage]

### 接口描述 [#接口描述-2]

`publishBinaryMessage` 方法用于向 Topic 发送 Binary 型消息，频道中订阅此 Topic 及此消息发布者的用户会在 100 毫秒内收到此消息。调用 `publishBinaryMessage` 方法需要用户首先加入 Stream Channel 并且通过调用 `joinTopic` 方法注册成为该 Topic 的消息发布者。

用户发送的消息在传输的过程中会被 TLS 加密，数据链路加密默认开启且无法关闭。用户也可以在出初始化的时候开启端侧加密，以获得更高的数据安全等级，详见[初始配置](/zh-CN/api-reference/rtm/flutter/configuration)。

### 接口方法 [#接口方法-2]

你可以通过以下方式调用 `publishBinaryMessage` 方法：

```dart
Future<(RtmStatus, PublishTopicMessageResult?)> publishBinaryMessage(
    String topic,
    Uint8List message,
    {
        int sendTs = 0,
        String? customType
    }
);
```

| 参数 | 类型 | 必填 | 默认值 | 描述 |
| --- | --- | --- | --- | --- |
| `topic` | String | 必填 | - | Topic 名称。 |
| `message` | String | 必填 | - | 消息负载。 |
| `sendTs` | int | 选填 | `0` | 消息发送时间戳。只有在 `joinTopic` 中设置 `syncWithMedia = true` 时，该参数才有效，SDK 会根据此时间戳与 RTC 音视频数据流进行数据同步。 |
| `customType` | String | 选填 | - | 用户自定义字段。仅支持 String 型。 |

### 基本用法 [#基本用法-2]

```dart
var payload = [1,2,3,4];
var (status,response) = await stChannel.publishBinaryMessage(
      "myTopic",
      payload,
      customType:"UINT8LIST"
    );
if (status.error == true) {
    print(status);
} else {
    print(response);
}
```

### 返回值 [#返回值-2]

调用此方法，会返回 `Future<(RtmStatus, PublishTopicMessageResult?)>` 类型的元组数据。

- 无论该方法是否调用成功，元组的第一项都会返回一个 `RtmStatus` 类型的数据，其中字段定义如下：

```dart
class RtmStatus {
    bool error;                   // 本次操作是否出错。
    String errorCode;             // 本次操作错误码。
    String operation;             // 本次操作的 API。
    String reason;                // 本次操作错误原因简述。
}
```

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

- 如果该方法调用成功，元组的第二项将返回一个 `PublishTopicMessageResult` 类型的数据，定义如下：

  ```dart
  class PublishTopicMessageResult {
      final String channelName;                           // 频道名称
      final String topic;                                 // Topic 名称
  }
  ```

## leaveTopic [#leavetopic]

### 接口描述 [#接口描述-3]

当你不再需要向某个 Topic 发布消息，你可以调用 `leaveTopic` 方法取消注册为该 Topic 的消息发布者，从而释放资源。该方法不影响你是否订阅该 Topic，也不影响其他用户对该 Topic 的任何操作。

成功调用该方法后，订阅该频道并开启事件监听的用户会收到 `remoteLeaveTopic` 类型的 `topic` 事件通知，详见 [事件监听](/zh-CN/api-reference/rtm/flutter/configuration#事件监听)。

### 接口方法 [#接口方法-3]

你可以通过以下方式调用 `leaveTopic` 方法：

```dart
Future<(RtmStatus, LeaveTopicResult?)> leaveTopic(String topic);
```

| 参数 | 类型 | 必填 | 默认值 | 描述 |
| --- | --- | --- | --- | --- |
| `topic` | String | 必填 | - | Topic 名称。 |

### 基本用法 [#基本用法-3]

```dart
var (status,response) = await stChannel.leaveTopic("myTopic");

if (status.error == true) {
    print(status);
} else {
    print(response);
}
```

### 返回值 [#返回值-3]

调用此方法，会返回 `Future<(RtmStatus, LeaveTopicResult?)>` 类型的元组数据。

- 无论该方法是否调用成功，元组的第一项都会返回一个 `RtmStatus` 类型的数据，其中字段定义如下：

```dart
class RtmStatus {
    bool error;                   // 本次操作是否出错。
    String errorCode;             // 本次操作错误码。
    String operation;             // 本次操作的 API。
    String reason;                // 本次操作错误原因简述。
}
```

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

- 如果该方法调用成功，元组的第二项将返回一个 `LeaveTopicResult` 类型的数据，定义如下：

  ```dart
  class LeaveTopicResult {
      final String channelName,                           // 频道名称
      final String userId,                                // 用户 ID
      final String topic,                                 // Topic 名称
      final String meta                                  // 附加数据
  }
  ```

## subscribeTopic [#subscribetopic]

### 接口描述 [#接口描述-4]

加入频道后，你可以调用 `subscribeTopic` 方法订阅该频道中 Topic 的消息发布者。

`subscribeTopic` 为增量方法。例如，第一次调用该方法时，订阅消息发布者列表为 `[UserA, UserB]`，第二次调用该方法时，订阅消息发布者列表为 `[UserB, UserC]`，则最后成功订阅的结果是 `[UserA, UserB, UserC]`。

同一个用户在同一个频道中最多只能同时订阅 50 个 Topic，且每个 Topic 中最多只能订阅 64 个消息发布者。详见 [API 调用限制](/zh-CN/realtime-media/rtm/reference/usage-limits-and-errors/api-limits)。

### 接口方法 [#接口方法-4]

你可以通过以下方式调用 `subscribeTopic` 方法：

```dart
Future<(RtmStatus, SubscribeTopicResult?)> subscribeTopic(
    String topic,
    {
        List<String> users = const []
    }
);
```

| 参数 | 类型 | 必填 | 默认值 | 描述 |
| --- | --- | --- | --- | --- |
| `topic` | String | 必填 | - | Topic 名称。 |
| `users` | `List<String>` | 选填 | - | 需要订阅的消息发布者（Publisher）的 `UserId` 列表，如果不设置，则默认随机订阅不超过 64 个用户。 |

### 基本用法 [#基本用法-4]

示例 1：订阅 Topic 中指定的消息发布者

```dart
var userList = ["Tony","Lily"];
var (status,response) = await stChannel.subscribeTopic("myTopic", users:userList);
if (status.error == true) {
    print(status);
} else {
    print(response);
}
```

示例 2：随机订阅 Topic 中的 64 个消息发布者

```dart
var (status,response) = await stChannel.subscribeTopic("myTopic");
if (status.error == true) {
    print(status);
} else {
    print(response);
}
```

### 返回值 [#返回值-4]

调用此方法，会返回 `Future<(RtmStatus, SubscribeTopicResult?)>` 类型的元组数据。

- 无论该方法是否调用成功，元组的第一项都会返回一个 `RtmStatus` 类型的数据，其中字段定义如下：

```dart
class RtmStatus {
    bool error;                   // 本次操作是否出错。
    String errorCode;             // 本次操作错误码。
    String operation;             // 本次操作的 API。
    String reason;                // 本次操作错误原因简述。
}
```

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

- 如果该方法调用成功，元组的第二项将返回一个 `SubscribeTopicResult` 类型的数据，定义如下：

  ```dart
  class SubscribeTopicResult {
      final String channelName,                           // 频道名称
      required this.userId,                               // 用户 ID
      final String topic,                                 // Topic 名称
      final List<String> succeedUsers;                    // 成功订阅用户列表
      final List<String> failedUsers;                     // 失败订阅用户列表
  }
  ```

## unsubscribeTopic [#unsubscribetopic]

### 接口描述 [#接口描述-5]

如果你对某个 Topic 不再感兴趣，或者不再需要订阅 Topic 中的一个或多个消息发布者，你可以调用 `unsubscribeTopic` 方法取消订阅该 Topic 或取消订阅该 Topic 中指定的消息发布者。

### 接口方法 [#接口方法-5]

你可以通过以下方式调用 `unsubscribeTopic` 方法：

```dart
Future<(RtmStatus, UnsubscribeTopicResult?)> unsubscribeTopic(
    String topic,
    {
        List<String> users = const []
    }
);
```

| 参数 | 类型 | 必填 | 默认值 | 描述 |
| --- | --- | --- | --- | --- |
| `topic` | String | 必填 | - | Topic 名称。 |
| `users` | `List<String>` | 选填 | - | 需要取消订阅的消息发布者（Publisher）的 `UserId` 列表，如果不设置，则默认随机取消订阅不超过 64 个用户。 |

### 基本用法 [#基本用法-5]

示例 1：取消订阅 Topic 中指定的消息发布者

```dart
var userList = ["Tony","Lily"];
var (status,response) = await stChannel.unsubscribeTopic("myTopic", users:userList);
if (status.error == true) {
    print(status);
} else {
    print(response);
}
```

示例 2：取消订阅 Topic 中的所有消息发布者

```dart
var (status,response) = await stChannel.unsubscribeTopic("myTopic");
if (status.error == true) {
    print(status);
} else {
    print(response);
}
```

### 返回值 [#返回值-5]

调用此方法，会返回 `Future<(RtmStatus, UnsubscribeTopicResult?)>` 类型的元组数据。

- 无论该方法是否调用成功，元组的第一项都会返回一个 `RtmStatus` 类型的数据，其中字段定义如下：

```dart
class RtmStatus {
    bool error;                   // 本次操作是否出错。
    String errorCode;             // 本次操作错误码。
    String operation;             // 本次操作的 API。
    String reason;                // 本次操作错误原因简述。
}
```

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

- 如果该方法调用成功，元组的第二项将返回一个 `UnsubscribeTopicResult` 类型的数据，定义如下：

  ```dart
  class UnsubscribeTopicResult {
      final String channelName,                           // 频道名称
      final String topic,                                 // Topic 名称
  }
  ```
