快速开始

大约 6 分钟

快速开始

通过本文可以实现一个集成聊天 SDK 的简单 app。

实现原理

下图展示在客户端发送和接收一对一文本消息的工作流程。

img

如上图所示,发送和接收单聊消息的步骤如下:

  1. 客户端向你的应用服务器请求 Token,你的应用服务器返回 Token。
  2. 客户端 A 和客户端 B 使用获得的 Token 登录即时通讯 IM。
  3. 客户端 A 发送消息到声网服务器。
  4. 声网服务器将消息发送到客户端 B,客户端 B 接收消息。

前提条件

开始前,请确保你的开发环境满足如下要求:

  • Xcode 12.4 或以上版本,包括命令行工具;
  • iOS 11 或以上版本;
  • Android SDK API 等级 21 或以上版本;
  • Android Studio 4.0 或以上版本,包括 JDK 1.8 或以上版本;
  • CocoaPods 包管理工具;
  • Flutter 3.3.0 或以上版本;
  • Dart 3.3.0 或以上版本;
  • 有效的即时通讯 IM 开发者账号和 App ID,详见 开通即时通讯服务

配置开发或者运行环境如果遇到问题,请参考 这里open in new window

项目设置

使用命令创建项目

打开终端,进入需要创建项目的目录,输入命令进行 flutter create 项目创建:

flutter create quick_start

设置 Android

  1. 打开文件 quick_start/android/app/build.gradle 在文件最后添加:
android {
    defaultConfig {
        minSdkVersion 21
    }
}
  1. 打开文件 quick_start/android/app/src/main/AndroidManifest.xml,在 </application> 下添加:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
  1. quick_start/android/app/proguard-rules.pro 中设置免混淆规则:
-keep class io.agora.** {*;}
-dontwarn  io.agora.**

设置 iOS

iOS 需要 iOS 11.0 以上版本,

打开文件 quick_start/ios/Runner.xcodeproj,修改:TARGETS -> General -> Deployment info, 设置 iOS 版本为 11.0。

集成 SDK

在终端命令行,输入命令添加依赖:

cd quick_start
flutter pub add shengwang_chat_sdk
flutter pub get

添加示例代码

打开 quick_start/lib/main.dart 文件,引入头文件:

import 'package:flutter/material.dart';
import 'package:shengwang_chat_sdk/shengwang_chat_sdk.dart';

修改 _MyHomePageState 代码:

class _MyHomePageState extends State<MyHomePage> {

  ScrollController scrollController = ScrollController();
  String _username = "";
  String _token = "";
  String _messageContent = "";
  String _chatId = "";
  final List<String> _logText = [];

  
  void initState() {
    super.initState();
    _initSDK();
    _addChatListener();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        padding: const EdgeInsets.only(left: 10, right: 10),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          mainAxisSize: MainAxisSize.max,
          children: [
            TextField(
              decoration: const InputDecoration(hintText: "Enter username"),
              onChanged: (username) => _username = username,
            ),
            TextField(
              decoration: const InputDecoration(hintText: "Enter token"),
              onChanged: (token) => _token = token,
            ),
            const SizedBox(height: 10),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                Expanded(
                  flex: 1,
                  child: TextButton(
                    onPressed: _signIn,
                    child: const Text("SIGN IN"),
                    style: ButtonStyle(
                      foregroundColor: MaterialStateProperty.all(Colors.white),
                      backgroundColor:
                          MaterialStateProperty.all(Colors.lightBlue),
                    ),
                  ),
                ),
                const SizedBox(width: 10),
                Expanded(
                  child: TextButton(
                    onPressed: _signOut,
                    child: const Text("SIGN OUT"),
                    style: ButtonStyle(
                      foregroundColor: MaterialStateProperty.all(Colors.white),
                      backgroundColor:
                          MaterialStateProperty.all(Colors.lightBlue),
                    ),
                  ),
                ),
              ],
            ),
            const SizedBox(height: 10),
            TextField(
              decoration: const InputDecoration(
                  hintText: "Enter the username you want to send"),
              onChanged: (chatId) => _chatId = chatId,
            ),
            TextField(
              decoration: const InputDecoration(hintText: "Enter content"),
              onChanged: (msg) => _messageContent = msg,
            ),
            const SizedBox(height: 10),
            TextButton(
              onPressed: _sendMessage,
              child: const Text("SEND TEXT"),
              style: ButtonStyle(
                foregroundColor: MaterialStateProperty.all(Colors.white),
                backgroundColor: MaterialStateProperty.all(Colors.lightBlue),
              ),
            ),
            Flexible(
              child: ListView.builder(
                controller: scrollController,
                itemBuilder: (_, index) {
                  return Text(_logText[index]);
                },
                itemCount: _logText.length,
              ),
            ),
          ],
        ),
      ),
    );
  }

  void _initSDK() async {
  }

  void _addChatListener() {
  }

  void _signIn() async {
  }

  void _signOut() async {
  }

  void _sendMessage() async {
  }

  void _addLogToConsole(String log) {
    _logText.add(_timeString + ": " + log);
    setState(() {
      scrollController.jumpTo(scrollController.position.maxScrollExtent);
    });
  }

  String get _timeString {
    return DateTime.now().toString().split(".").first;
  }
}

初始化 SDK

_initSDK 方法中添加 SDK 初始化:

void _initSDK() async {
    ChatOptions options = ChatOptions.withAppId(
        "<#Your AppId#>",
        autoLogin: false,
    );
    await ChatClient.getInstance.init(options);
    // 通知 SDK UI 已准备好。该方法执行后才会收到 `ChatRoomEventHandler`、`ChatContactEventHandler` 和 `ChatGroupEventHandler` 回调。
    await ChatClient.getInstance.startCallback();
}

注册即时通讯 IM 用户

创建用户

声网控制台open in new window按照如下步骤创建用户:

  1. 展开控制台左上角下拉框,选择需要开通即时通讯 IM 服务的项目。

  2. 点击左侧导航栏的全部产品

  3. 在下拉列表中找到即时通讯 IM 并点击。

  4. 即时通讯 IM 页面,进入运营管理标签页。

  5. 用户 页签下,点击创建IM用户

  6. 在弹出的对话框中,配置用户相关参数,点击确定

img

获取用户 token

创建用户后,在用户列表点击对应的用户的操作一栏中的更多,选择查看Token

在弹出的对话框中,可以查看用户 Token,也可以点击重新生成,生成用户 token。

img

在生产环境中,为了安全考虑,你需要部署 App Server 生成 Token,详见 Token 鉴权文档

添加登录

_signIn 方法中添加登录代码。

void _signIn() async {
    if (_username.isEmpty || _token.isEmpty) {
        _addLogToConsole("username or token is null");
        return;
    }

    try {
        await ChatClient.getInstance.loginWithToken(_username, _token);
        _addLogToConsole("sign in succeed, username: $_username");
    } on ChatError catch (e) {
        _addLogToConsole("sign in failed, e: ${e.code} , ${e.description}");
    }
}

添加退出

_signOut 方法中添加退出代码。

void _signOut() async {
    try {
        await ChatClient.getInstance.logout(true);
        _addLogToConsole("sign out succeed");
    } on ChatError catch (e) {
        _addLogToConsole(
            "sign out failed, code: ${e.code}, desc: ${e.description}");
    }
}

添加发消息

_sendMessage 方法中添加发消息代码。

void _sendMessage() async {
  if (_chatId.isEmpty || _messageContent.isEmpty) {
    _addLogToConsole("single chat id or message content is null");
    return;
  }

  var msg = ChatMessage.createTxtSendMessage(
    targetId: _chatId,
    content: _messageContent,
  );

  ChatClient.getInstance.chatManager.sendMessage(msg);
}

添加收消息监听

_addChatListener 方法中添加代码。

void _addChatListener() {

  // 添加消息状态变更监听
  ChatClient.getInstance.chatManager.addMessageEvent(
      // ChatMessageEvent 对应的 key。
        "UNIQUE_HANDLER_ID",
        ChatMessageEvent(
          onSuccess: (msgId, msg) {
            _addLogToConsole("send message succeed");
          },
          onProgress: (msgId, progress) {
            _addLogToConsole("send message succeed");
          },
          onError: (msgId, msg, error) {
            _addLogToConsole(
              "send message failed, code: ${error.code}, desc: ${error.description}",
            );
          },
        ));


  // 添加收消息监听
  ChatClient.getInstance.chatManager.addEventHandler(
    // ChatEventHandler 对应的 key。
    "UNIQUE_HANDLER_ID",
    ChatEventHandler(
      onMessagesReceived: (messages) {
        for (var msg in messages) {
          switch (msg.body.type) {
            case MessageType.TXT:
              {
                ChatTextMessageBody body = msg.body as ChatTextMessageBody;
                _addLogToConsole(
                  "receive text message: ${body.content}, from: ${msg.from}",
                );
              }
              break;
            case MessageType.IMAGE:
              {
                _addLogToConsole(
                  "receive image message, from: ${msg.from}",
                );
              }
              break;
            case MessageType.VIDEO:
              {
                _addLogToConsole(
                  "receive video message, from: ${msg.from}",
                );
              }
              break;
            case MessageType.LOCATION:
              {
                _addLogToConsole(
                  "receive location message, from: ${msg.from}",
                );
              }
              break;
            case MessageType.VOICE:
              {
                _addLogToConsole(
                  "receive voice message, from: ${msg.from}",
                );
              }
              break;
            case MessageType.FILE:
              {
                _addLogToConsole(
                  "receive image message, from: ${msg.from}",
                );
              }
              break;
            case MessageType.CUSTOM:
              {
                _addLogToConsole(
                  "receive custom message, from: ${msg.from}",
                );
              }
              break;
            case MessageType.COMBINE:
              {
                _addLogToConsole(
                  "receive combine message, from: ${msg.from}",
                );
              }
              break;
            case MessageType.CMD:
              {
                // 当前回调中不会有 CMD 类型消息,CMD 类型消息通过 `ChatEventHandler#onCmdMessagesReceived` 回调接收
              }
              break;
          }
        }
      },
    ),
  );
}

移除消息监听

dispose 方法中添加代码移除监听:


void dispose() {
  // 移除消息状态监听
  ChatClient.getInstance.chatManager.removeMessageEvent("UNIQUE_HANDLER_ID");
  // 移除收消息监听
  ChatClient.getInstance.chatManager.removeEventHandler("UNIQUE_HANDLER_ID");
  super.dispose();
}

运行项目

以 iOS 为例,首先打开模拟器,然后在终端运行以下命令。

flutter run

运行结果如下:

参考以下步骤发送和接收文本消息:

  1. 输入任意用户 ID(如 flutter001flutter002)和密码 1,点击 SIGN UP 创建用户。
  2. flutter001 身份登录 Demo,将 Enter the username you want to send 输如为 flutter002,发送文本消息。
  1. flutter002 身份登录 Demo,查看 Log 信息确认是否都到消息。

后续步骤

为保障通信安全,在正式生产环境中,你需要在自己的 app 服务端生成 Token。详见使用 Token 鉴权

上次编辑于: