融云即时通讯 Flutter 插件

本存储库为融云即时通讯的 Flutter 插件。插件基于融云 iOS/Android 平台的 IMLib SDK。

更新提示

  • 从 2.0.0 开始废弃核心类 RongcloudImPlugin,新的核心类名为 RongIMClient

  • 从 1.1.0 开始为方便排查 Android 问题将即时通讯 Flutter SDK Android 的包名改为 io.rong.flutter.imlib

完整发版历史,请移步 CHANGELOG.md

获取 App Key

应用需要有 App Key 才能换取客户端连接融云服务器的身份凭证。通过开发者后台获取 App Key

导入 SDK

  1. 在项目的 pubspec.yaml 中,添加融云即时通讯 Flutter 插件为依赖项:

    dependencies:
      flutter:
        sdk: flutter
       
    rongcloud_im_plugin: ^5.1.8+7
    
  2. 在项目路径下,下载插件:

    flutter packages get
    

初始化 SDK

使用从开发者后台获取的 App Key 初始化 SDK。

RongIMClient.init(RongAppKey);

连接 IM

连接融云服务器。

RongIMClient.connect(RongIMToken, (int code, String userId) {
  print('connect result ' + code.toString());
  EventBus.instance.commit(EventKeys.UpdateNotificationQuietStatus, {});
if (code == 31004 || code == 12) {
  Navigator.of(context).pushAndRemoveUntil(new MaterialPageRoute(builder: (context) => new LoginPage()), (route) => route == null);
} else if (code == 0) {
  print("connect userId" + userId);
  // 连接成功后打开数据库
  // _initUserInfoCache();
}

API 调用

断开 IM 连接

//needPush 断开连接之后是否需要远程推送
RongIMClient.disconnect(bool needPush)

发送消息

发送文本消息

onSendMessage() async{
      TextMessage txtMessage = new TextMessage();
      txtMessage.content = "这条消息来自 Flutter";
      Message msg = await RongIMClient.sendMessage(RCConversationType.Private, privateUserId, txtMessage);
      print("send message start senderUserId = "+msg.senderUserId);
  }

发送图片消息

onSendImageMessage() async {
    ImageMessage imgMessage = new ImageMessage();
    imgMessage.localPath = "image/local/path.jpg";
    Message msg = await RongIMClient.sendMessage(RCConversationType.Private, privateUserId, imgMessage);
    print("send image message start senderUserId = "+msg.senderUserId);
  }

发送 Gif 消息

onSendGifMessage() async {
    GIFMessage gifMessage = GifMessage.obtain("gif/local/path.jpg");
    Message msg = await RongIMClient.sendMessage(RCConversationType.Private, privateUserId, gifMessage);
    print("send gif message start senderUserId = "+msg.senderUserId);
  }

发送文件消息

onSendFileMessage() async {
    // localPath 为文件本地路径,注意 Android 必须以 file:// 开头
    FileMessage fileMessage = FileMessage.obtain(localPaht);
    // 文件后缀如 "png" "txt"
    fileMessage.mType = "XXX";
    Message msg = await RongIMClient.sendMessage(
              conversationType, targetId, fileMessage);
  }

发送结果回调

//消息发送结果回调
    RongIMClient.onMessageSend = (int messageId,int status,int code) {
      print("send message messsageId:"+messageId.toString()+" status:"+status.toString()+" code:"+code.toString());
    };

媒体消息媒体文件上传进度

//媒体消息(图片/语音消息)上传媒体进度的回调
    RongIMClient.onUploadMediaProgress = (int messageId,int progress) {
      print("upload media messsageId:"+messageId.toString()+" progress:"+progress.toString());
    };

接收消息

注意:以下两个接收消息的回调只能实现一个,否则会重复收到消息。

如果离线消息量不大,可以使用下面这个回调。

//消息接收回调
    RongIMClient.onMessageReceived = (Message msg,int left) {
      print("receive message messsageId:"+msg.messageId.toString()+" left:"+left.toString());
    };

离线消息量巨大时,建议使用以下回调分批拉取离线消息。建议在 left == 0 且 hasPackage == false 时刷新会话列表。

//消息接收回调
    RongIMClient.onMessageReceivedWrapper = (Message msg, int left, bool hasPackage, bool offline) {
      print("receive message messsageId:"+msg.messageId.toString()+" left:"+left.toString());
    };

历史消息

获取本地历史消息

onGetHistoryMessages() async {
    List msgs = await RongIMClient.getHistoryMessage(RCConversationType.Private, privateUserId, 0, 10);
    print("get history message");
    for(Message m in msgs) {
      print("sentTime = "+m.sentTime.toString());
    }
  }

获取远端历史消息

RongIMClient.getRemoteHistoryMessages(1, "1001", 0, 20,(List<Message> msgList,int code) {
      if(code == 0) {
        for(Message msg in msgList) {
          print("getRemoteHistoryMessages  success "+ msg.messageId.toString());
        }
      }else {
        print("getRemoteHistoryMessages error "+code.toString());
      }
    });

插入发出的消息

RongIMClient.insertOutgoingMessage(RCConversationType.Private, "1001", 10, msgT, 0, (msg,code){
      print("insertOutgoingMessage " + msg.content.encode() + " code " + code.toString());

    });

插入收到的消息

RongIMClient.insertIncomingMessage(RCConversationType.Private, "1002", "1002", 1, msgT , 0, (msg,code){
      print("insertIncomingMessage " + msg.content.encode() + " code " + code.toString());
    });

删除特定会话消息

RongIMClient.deleteMessages(RCConversationType.Private, "2002", (int code) {

});

批量删除消息

List<int> mids =  [];
mids.add(1);
RongIMClient.deleteMessageByIds(mids, (int code) {

});

未读数

获取特定会话的未读数

RongIMClient.getUnreadCount(RCConversationType.Private, "targetId", (int count,int code) {
      if( 0 == code) {
        print("未读数为"+count.toString());
      }
    });

获取特定会话类型的未读数

RongIMClient.getUnreadCountConversationTypeList([RCConversationType.Private,RCConversationType.Group], true, (int count, int code) {
      if( 0 == code) {
        print("未读数为"+count.toString());
      }
    });

获取所有未读数

RongIMClient.getTotalUnreadCount((int count, int code) {
      if( 0 == code) {
        print("未读数为"+count.toString());
      }
    });

会话列表

获取会话列表

onGetConversationList() async {
    List conversationList = await RongIMClient.getConversationList([RCConversationType.Private,RCConversationType.Group,RCConversationType.System]);

    for(Conversation con in cons) {
      print("conversation latestMessageId " + con.latestMessageId.toString());
    }
  }

删除指定会话

RongIMClient.removeConversation(RCConversationType.Private, "1001", (success) {
      if(success) {
        print("删除会话成功");
      }
    });

黑名单

把用户加入黑名单

RongIMClient.addToBlackList(blackUserId, (int code) {
      print("_addBlackList:" + blackUserId + " code:" + code.toString());
    });

把用户移出黑名单

RongIMClient.removeFromBlackList(blackUserId, (int code) {
      print("_removeBalckList:" + blackUserId + " code:" + code.toString());
    });

查询特定用户的黑名单状态

RongIMClient.getBlackListStatus(blackUserId,
        (int blackStatus, int code) {
      if (0 == code) {
        if (RCBlackListStatus.In == blackStatus) {
          print("用户:" + blackUserId + " 在黑名单中");
        } else {
          print("用户:" + blackUserId + " 不在黑名单中");
        }
      } else {
        print("用户:" + blackUserId + " 黑名单状态查询失败" + code.toString());
      }
    });

查询已经设置的黑名单列表

RongIMClient.getBlackList((List/*<String>*/ userIdList, int code) {
      print("_getBlackList:" + userIdList.toString() + " code:" + code.toString());
      userIdList.forEach((userId) {
        print("userId:"+userId);
      });
    });

聊天室

加入聊天室

onJoinChatRoom() {
    RongIMClient.joinChatRoom("testchatroomId", 10);
  }

加入聊天室回调

//加入聊天室结果回调
    RongIMClient.onJoinChatRoom = (String targetId,int status) {
      print("join chatroom:"+targetId+" status:"+status.toString());
    };

退出聊天室

onQuitChatRoom() {
    RongIMClient.quitChatRoom("testchatroomId");
  }

退出聊天室回调

//退出聊天室结果回调
    RongIMClient.onQuitChatRoom = (String targetId,int status) {
      print("quit chatroom:"+targetId+" status:"+status.toString());
    };

获取聊天室信息

onGetChatRoomInfo() async {
    ChatRoomInfo chatRoomInfo = await RongIMClient.getChatRoomInfo("testchatroomId", 10, RCChatRoomMemberOrder.Desc);
    print("onGetChatRoomInfo targetId ="+chatRoomInfo.targetId);
  }

Native 向 Flutter 传递数据

iOS 端传递数据:

[[RCIMFlutterWrapper sharedWrapper] sendDataToFlutter:@{@"key":@"ios"}];

Android 端传递数据:

Map map = new HashMap();
map.put("key","android");
RCIMFlutterWrapper.getInstance().sendDataToFlutter(map);

Flutter 端接收数据:

RongIMClient.onDataReceived = (Map map) {
  print("object onDataReceived " + map.toString());
};

单聊已读回执

发送已读回执:

RongIMClient.sendReadReceiptMessage(conversationType, targetId, timestamp, (int code){
  if (code == 0) {
    print('sendReadReceiptMessageSuccess');
  } else {
    print('sendReadReceiptMessageFailed:code = + $code');
  }
});

接收已读回执:

RongIMClient.onReceiveReadReceipt = (Map map) {
  print("object onReceiveReadReceipt " + map.toString());
};

群组已读回执

如何实现群组已读回执

消息撤回

撤回消息调用如下接口会返回 RecallNotificationMessage 类型的消息体,需要把原有消息的内容替换,刷新 UI 显示为此类型消息的展示。

void _recallMessage(Message message) async {
    RecallNotificationMessage recallNotifiMessage =
        await RongIMClient.recallMessage(message, "");
    if (recallNotifiMessage != null) {
      message.content = recallNotifiMessage;
      _insertOrReplaceMessage(message);
    } else {
      showShortToast("撤回失败");
    }
  }

聊天室属性自定义

详细参见聊天室存储相关接口

消息搜索

首先搜索关键词相关的会话信息。

static void searchConversations(
      String keyword,
      List conversationTypes,
      List objectNames,
      Function(int code, List searchConversationResult) finished)

接着,根据搜索会话返回的信息,针对某个会话搜索相应会话的消息。

static void searchMessages(
      int conversationType,
      String targetId,
      String keyword,
      int count,
      int beginTime,
      Function(List/*<Message>*/ msgList, int code) finished) 

全局消息提醒

全局屏蔽某个时间段的消息提醒

void _setNotificationQuietHours() {
    RongIMClient.setNotificationQuietHours("09:00:00", 600,
        (int code) {
      String toast = "设置全局屏蔽某个时间段的消息提醒:\n" +
          (code == 0 ? "设置成功" : "设置失败, code:" + code.toString());
      print(toast);
    });
  }

查询已设置的全局时间段消息提醒屏蔽

  void _getNotificationQuietHours() {
    RongIMClient.getNotificationQuietHours(
        (int code, String startTime, int spansMin) {
      String toast = "查询已设置的全局时间段消息提醒屏蔽\n: startTime:" +
          startTime +
          " spansMin:" +
          spansMin.toString() +
          (code == 0 ? "" : "\n设置失败, code:" + code.toString());
      print(toast);
    });
  }

删除已设置的全局时间段消息提醒屏蔽

  void _removeNotificationQuietHours() {
    RongIMClient.removeNotificationQuietHours((int code) {
      String toast = "删除已设置的全局时间段消息提醒屏蔽:\n" +
          (code == 0 ? "删除成功" : "删除失败, code:" + code.toString());
      print(toast);
    });
  }

获取会话中@提醒自己的消息

getUnreadMentionedMessages(int conversationType, String targetId) {
  Future<List> messages = RongIMClient.getUnreadMentionedMessages(conversationType, targetId);
  print("get unread mentioned messages = " + messages.toString());
}

发送群定向消息

  1. 此方法用于在群组中发送消息给其中的部分用户,其它用户不会收到这条消息。
  2. 此方法目前仅支持群组。
  3. 群定向消息不存储到云端,通过“单群聊消息云存储”服务无法获取到定向消息。
onSendDirectionalMessage() async {
    TextMessage txtMessage = new TextMessage();
    txtMessage.content = "这条消息来自 Flutter 的群定向消息";
    Message message = await RongIMClient.sendDirectionalMessage(
        RCConversationType.Group, targetId, ['UserId1', 'UserId2'], txtMessage);
    print("send directional message start senderUserId = " + msg.senderUserId);
  }

设置断线重连时是否踢出当前正在重连的设备

设置 enable 为 YES 时,SDK 重连的时候发现此时已有别的设备连接成功,不再强行踢出已有设备,而是踢出重连设备。

RongIMClient.setReconnectKickEnable(true);

获取当前 SDK 的连接状态

void getConnectionStatus() async {
  int status = await RongIMClient.getConnectionStatus();
  print('getConnectionStatus: $status');
}

取消下载中的媒体文件

RongIMClient.cancelDownloadMediaMessage(100);

从服务器端获取聊天室的历史消息

void _getChatRoomHistoryMessage() {
  RongIMClient.getRemoteChatroomHistoryMessages(
      targetId, 0, 20, RCTimestampOrder.RC_Timestamp_Desc,
      (List/*<Message>*/ msgList, int syncTime, int code) {
    DialogUtil.showAlertDiaLog(
        context,
        "获取聊天室历史消息:code:" +
            CodeUtil.codeString(code) +
            ",msgListCount:${msgList.length} 条消息\n" +
            ",msgList:$msgList" +
            ",syncTime:$syncTime");
  });
}

通过 messageUId 获取消息实体

messageUID: 发送 message 成功后,服务器会给每个 message 分配一个唯一 messageUId。

Message msg = await RongIMClient.getMessageByUId(message.messageUId);

删除指定的一条或者一组消息

该操作会同时删除本地和远端消息 (会话类型不支持聊天室)。

RongIMClient.deleteRemoteMessages(conversationType, targetId, messageList, (code){
    print("result: $code");
    });

清空指定会话的聊天记录

该操作要求指定会话的类型和 targetId。

RongIMClient.clearMessages(con.conversationType, con.targetId, (code) {
    print("result:$code");
    });

设置本地消息的附加信息(message.extra)

RongIMClient.setMessageExtra(int messageId, value, (code) {
    print("result:$code");
    });

设置接收到的消息状态

根据 messageId 设置接收到的消息状态。用于 UI 标记消息为已读、已下载等状态。

RongIMClient.setMessageReceivedStatus(message.messageId, 1, (code) async{
    print("setMessageReceivedStatus result:$code");
    });

设置消息的发送状态

根据 messageId 设置消息的发送状态。用于 UI 标记消息为正在发送、对方已接收等状态。

RongIMClient.setMessageSentStatus(message.messageId, 1, (code) async{
    print("setMessageReceivedStatus result:$code");
    });

清空会话类型列表中的所有会话及会话信息

RongIMClient.clearConversations(conversations, (code) async{
    print("clearConversations result:$code");
    });

获取本地时间与服务器时间的差值

消息发送成功后,SDK 会与服务器同步时间,消息所在数据库中存储的时间就是服务器时间。

int deltaTime = await RongIMClient.getDeltaTime()

设置当前用户离线消息补偿时间

RongIMClient.setOfflineMessageDuration(3, (code, result){
    print("setOfflineMessageDuration code:$code result:$result");
    });

获取当前用户离线消息的存储时间

离线消息的存储时间取值范围为 int 值 1~7 天。

int duration = await RongIMClient.getOfflineMessageDuration();

文档

支持

源码地址 Github