# 实现收发消息 (/zh-CN/realtime-media/rtm/get-started/quick-start/flutter)

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

本文将指导你如何利用 RTM Flutter SDK 构建简单的应用程序，内容涵盖了集成和开发的基础知识：开通声网账号、获取 SDK、发送消息、接收消息等。
同时，我们也提供了开源的实时消息示例项目供你参考，你可以前往下载或查看其中的源代码。

<Cards>
  <Card title="Agora-RTM2-QuickStart" href="https://github.com/AgoraIO/RTM2/tree/main/Agora-RTM2-QuickStart-Flutter" />
</Cards>

## 准备工作 [#准备工作-5]

开始构建项目前，你需要完成[开通 RTM 服务](/zh-CN/realtime-media/rtm/build/rtm-initialization/enable-service)，并检查你的开发环境是否满足[平台支持](/zh-CN/realtime-media/rtm/reference/capabilities-and-compatibility/platform-support)中的最低版本要求。

## 构建项目 [#构建项目-4]

### 1. 创建项目 [#1-创建项目]

根据以下步骤创建并配置项目：

1. 打开终端或命令行，执行以下命令来创建一个新的 Flutter 项目：

    ```shell
    # 创建 Flutter 项目
    flutter create getting_started
    ```

2. 使用的你的 IDE 工具打开项目，编辑 `pubspec.yaml` 文件，添加以下依赖：

    ```yaml
    dependencies:
      # 添加 RTM Flutter SDK 依赖项，请使用最新版本的 agora_rtm
      agora_rtm: ^2.2.6
    ```

   :::info[信息]
   如果你同时集成了 2.2.1 及以上版本的 RTM SDK 和 4.3.0 及以上版本的 RTC SDK，请参考 [FAQ](/zh-CN/reference/faq/integration/rtm2_rtc_integration_issue) 处理集成问题。
   :::

3. 添加依赖并保存文件后，打开终端，在项目路径下执行以下命令获取并安装依赖：

    ```shell
    flutter pub get
    ```

打开 `main.dart` 文件，删除其中所有代码，并将以下代码复制进去，作为项目的模板文件。

    ```dart
    import 'package:agora_rtm/agora_rtm.dart';
    import 'package:flutter/material.dart';
    import 'dart:convert';

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();        // 等待 Widgets 初始化完成。

      // create rtm instance

      // login rtm service

      // subscribe to a channel

      // publish messages

      // unsubscribe frome a channel

      // logout rtm service

    }
   ```

:::info[信息]
（仅 iOS）在后续构建和发布 IPA 文件阶段，为避免因符号剥离而引发的问题，你还需要在 Xcode 中导航至 **Target Runner > Build Settings > Strip Style**，并将其设置为 **Non-Global Symbols**。
:::

### 2. 创建并初始化 RTM [#2-创建并初始化-rtm]

调用 RTM SDK 的任何 API 之前，需要先创建一个 <code>RtmClient</code> 对象实例。添加以下代码到 `main.dart` 文件中，注意：请将 `your_appId`、`your_userId` 替换成你的 `appId` 和 `userId`.

```dart
import 'package:agora_rtm/agora_rtm.dart';
import 'package:flutter/material.dart';
import 'dart:convert';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();        // 等待 Widgets 初始化完成。

  const userId = 'your_userId';
  const appId = 'your_appId';
  const channelName = 'getting-started';
  late RtmClient rtmClient;

  //create rtm instance
  try {
    // create rtm client
    final (status, client) = await RTM( appId, userId);
    if (status.error == true) {
        print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
    } else {
        rtmClient = client;
        print('Initialize success!');
    }
  // add events listner

  } catch (e) {
    print('Initialize falid!:${e}');
  }
}
```

### 3. 添加事件监听 [#3-添加事件监听-3]

事件监听程序帮助你实现频道中消息、事件到达后的处理逻辑，添加以下代码到你的程序中以显示收到的消息或事件通知：

```dart showLineNumbers
// Paste the following code snippet below "add event listener" comment
rtmClient.addListener(
  // add message event handler
  message: (event) {
    print('recieved a message from channel: ${event.channelName}, channel type : ${event.channelType}');
    print('message content: ${utf8.decode(event.message!)}, custome type: ${event.customType}');
  },
  // add link state event handler
  linkState: (event) {
    print('link state changed from ${event.previousState} to ${event.currentState}');
    print('reason: ${event.reason}, due to operation ${event.operation}');
  });
```

### 4. 登录服务 [#4-登录服务-3]

你需要执行登录操作才能建立与 RTM 服务器的连接，然后才能调用 SDK 的其他 API。将以下代码添加到程序中：

```dart showLineNumbers
// Paste the following code snippet below "login rtm service" comment
try {
  // login rtm service
  var (status,response) = await rtmClient.login(appId);
  if (status.error == true) {
      print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
  } else {
      print('login RTM success!');
  }
} catch (e) {
  print('Failed to login: $e');
}

```

:::info[信息]
你需要在登录时传入 Token。在测试阶段，为快速验证功能，我们建议你在创建声网项目时将鉴权模式设置为调试模式，然后在登录时只需传入项目的 App ID。
:::

### 5. 收发消息 [#5-收发消息-3]

调用 <code>publish</code> 方法向 Message Channel 发送消息后，RTM 会把该消息分发给该频道的所有订阅者，以下代码演示如何发送字符串类型的消息，将此代码片段添加到程序中：

:::info[信息]
你需要先对消息负载进行字符串序列化，才能调用 <code>publish</code> 方法发送消息。
:::


```dart showLineNumbers
// Paste the following code snippet below "Publish a message" comment
// Send a message every second for 100 seconds
for (var i = 0; i < 100; i++) {
  try {
    var (status, response) = await rtmClient.publish(
        channelName,
        'message number : $i',
        channelType: RtmChannelType.message,
        customType: 'PlainText'
        );
    if (status.error == true ){
      print('${status.operation} failed, errorCode: ${status.errorCode}, due to ${status.reason}');
    } else {
      print('${status.operation} success! message number:$i');
    }
  } catch (e) {
    print('Failed to publish message: $e');
  }
  await Future.delayed(Duration(seconds: 1));
}
```

调用 <code>subscribe</code> 方法订阅此频道以接收此频道中的消息。将以下代码添加到程序中：

```dart showLineNumbers
// Paste the following code snippet below "subscribe to a channel" comment
try {
  // subscribe to 'getting-started' channel
  var (status,response) = await rtmClient.subscribe(channelName);
  if (status.error == true) {
      print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
  } else {
      print('subscribe channel: ${channelName} success!');
  }
} catch (e) {
  print('Failed to subscribe channel: $e');
}

```

如果你不再需要在此频道收发消息，你可以调用 <code>unsubscribe</code> 方法取消订阅此频道。

```dart showLineNumbers
// Paste the following code snippet below "unsubscribe frome a channel" comment
try {
  // unsubscribe channel
  var (status,response) = await rtmClient.unsubscribe(channelName);
  if (status.error == true) {
      print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
  } else {
      print('unsubscribe success!');
  }
} catch (e) {
  print('something went wrong with logout: $e');
}
```

如需了解更多收发消息的信息，查看[消息](/zh-CN/api-reference/rtm/flutter/message)。

### 6. 登出服务 [#6-登出服务-2]

当不再需要使用 RTM 服务时，你可以调用 <code>logout</code> 方法登出 RTM 系统。将以下代码添加到程序中：

```dart
// Paste the following code snippet below "logout rtm service" comment
try {
  // logout rtm service
  var (status,response) = await rtmClient.logout();
  if (status.error == true) {
      print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
  } else {
      print('logout RTM success!');
  }
} catch (e) {
  print('something went wrong with logout: $e');
}
```


:::info[信息]
本操作会影响你账单中的 PCU 计费项。
:::


### 7. 组合到一起 [#7-组合到一起-2]

经过上述步骤，你程序中的代码应该如下所示：

```dart
import 'package:flutter/material.dart';
import 'package:agora_rtm/agora_rtm.dart';
import 'dart:convert';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  const userId = 'your_userId';
  const appId = 'your_appId';
  const channelName = 'getting-started';
  late RtmClient rtmClient;

  //create rtm instance
  try {
    // create rtm client
    final (status, client) = await RTM( appId, userId);
    if (status.error == true) {
        print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
    } else {
        rtmClient = client;
        print('Initialize success!');
    }

    // add events listner
    rtmClient.addListener(
    // add message event handler
    message: (event) {
      print('recieved a message from channel: ${event.channelName}, channel type : ${event.channelType}');
      print('message content: ${utf8.decode(event.message!)}, custome type: ${event.customType}');
    },
    // add link state event handler
    linkState: (event) {
      print('link state changed from ${event.previousState} to ${event.currentState}');
      print('reason: ${event.reason}, due to operation ${event.operation}');
    });
  } catch (e) {
    print('Initialize falid!:${e}');
  }

  // login rtm service
  try {
    // login rtm service
    var (status,response) = await rtmClient.login(appId);
    if (status.error == true) {
        print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
    } else {
        print('login RTM success!');
    }
  } catch (e) {
    print('Failed to login: $e');
  }

  // subscribe to a channel
  try {
    // subscribe to 'getting-started' channel
    var (status,response) = await rtmClient.subscribe(channelName);
    if (status.error == true) {
        print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
    } else {
        print('subscribe channel: ${channelName} success!');
    }
  } catch (e) {
    print('Failed to subscribe channel: $e');
  }

  // publish messages
  // Send a message every second for 100 seconds
  for (var i = 0; i < 100; i++) {
    try {
      var (status, response) = await rtmClient.publish(
          channelName,
          'message number : $i',
          channelType: RtmChannelType.message,
          customType: 'PlainText'
          );
      if (status.error == true ){
        print('${status.operation} failed, errorCode: ${status.errorCode}, due to ${status.reason}');
      } else {
        print('${status.operation} success! message number:$i');
      }
    } catch (e) {
      print('Failed to publish message: $e');
    }
    await Future.delayed(Duration(seconds: 1));
  }

  // unsubscribe frome a channel
  try {
    // unsubscribe channel
    var (status,response) = await rtmClient.unsubscribe(channelName);
    if (status.error == true) {
        print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
    } else {
        print('unsubscribe success!');
    }
  } catch (e) {
    print('something went wrong with logout: $e');
  }

  // logout rtm service
  try {
    // logout rtm service
    var (status,response) = await rtmClient.logout();
    if (status.error == true) {
        print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
    } else {
        print('logout RTM success!');
    }
  } catch (e) {
    print('something went wrong with logout: $e');
  }
}
```

现在，你可以开始运行你的程序：

1. 保存项目。
2. 将目标设备连接到电脑，或者打开模拟器。
3. 打开终端，在项目路径下执行以下命令在目标设备上运行示例项目：

   ```shell
   flutter run
   ```

   成功运行后，在终端上你将看到类似以下信息：

    ```shell
    flutter: publish success! message number:0
    flutter: publish success! message number:1
    flutter: publish success! message number:2
    flutter: publish success! message number:3
    ```

4. 复制项目，在 IDE 上打开，编辑 `main.dart` 文件，修改 `userId` 保存后按照上述步骤运行第二个进程，观察终端上的输出，你将收到类似以下信息：

   ```shell
   flutter: recieved a message from channel: getting-started, channel type : RtmChannelType.message
   flutter: message content: message number : 0, custome type: PlainText
   flutter: recieved a message from channel: getting-started, channel type : RtmChannelType.message
   flutter: message content: message number : 1, custome type: PlainText
   ```

至此，你已经成功集成并正确使用了 RTM 服务。

## 贯穿始终 [#贯穿始终-5]

相比于介绍如何写出代码，声网更愿意帮助你掌握上述程序编写的过程和逻辑。上述程序依次完成了以下操作，让你可以正确地收发消息：

1. 创建并初始化 RTM 对象。
2. 添加 `linkState`、`message` 事件监听函数。
3. 调用 `login` 登录了 RTM 服务。
4. 调用 `subscribe` 订阅了一个 Message Channel。
5. 调用 `publish` 发送消息。
6. 调用 `unsubscribe` 取消订阅一个 Message Channel。
7. 调用 `logout` 登出 RTM 系统。

## 下一步 [#下一步-5]

现在，你已经学会了如何使用 RTM Flutter SDK 来实现 Message Channel 的收发消息功能。下一步，你可以通过 SDK 的 [API 参考](/zh-CN/api-reference/rtm/flutter/configuration)了解更多功能的使用方法。






