作者:隋晓旭

你是否想过从头开发一款相似 QQ、微信的聊天利用?又或者,想要在你开发的利用中退出聊天性能,不便用户交换,加强用户粘性?那么,这篇文章就是为你筹备的。在这篇文章中,我将介绍如何基于 Flutter 疾速实现一款聊天利用。

利用简介

这个基于 Flutter 开发的利用还在继续欠缺中,现曾经反对如下性能:

  • 登录、登出
  • 发动单聊
  • 发动群聊
  • 反对文字音讯、语音音讯、图片音讯
  • 反对展现未读音讯数
  • 反对展示会话成员
  • 反对批改会话名称
  • 反对离线音讯推送
  • 投诉举报不良信息
  • 把某用户退出黑名单

页面截图

开发环境搭建

第一步:Flutter 装置和环境搭建间接查看: Flutter 文档。
第二步:登录 LeanCloud 控制台,创立 LeanCloud 利用。

  • 在控制台 > 利用 > 设置 >域名绑定页面绑定 API 拜访域名。临时没有域名能够略过这一步,LeanCloud 也提供了短期无效的收费体验域名;或者注册 LeanCloud 国际版,国际版不要求绑定域名。
  • 在控制台 > 利用 > 设置 > 利用 Keys 页面记录 AppID、AppKey 与服务器地址备用,这里的服务器地址就是 REST API 服务器地址。如果未绑定域名,控制台雷同的地位能够获取到收费的共享域名。

APP 初始化设置

在 pubspec.yaml 中,将 LeanCloud Flutter SDK 增加到依赖项列表:

dependencies:  leancloud_official_plugin: ^1.0.0-beta.8   //即时通信插件  leancloud_storage: ^0.2.9  //数据存储 SDK

而后运行 flutter pub get 装置 SDK。

因为 leancloud_official_plugin 是基于 Swift SDK 以及 Java Unified SDK 开发,所以还要装置前面两个 SDK,这样利用能力别离在 iOS 和 Android 设施运行。

须要通过 CocoaPods 装置 Swift SDK,这一步和装置 iOS 其余第三方库是一样的,在利用的 ios 目录下执行 pod update 即可。

$ cd ios/$ pod update # 或者 $ pod install --repo-update

同样的,须要配置 Gradle 来装置 Java Unified SDK,关上工程目录 android/app/build.gradle,增加如下依赖,用 Android Studio 关上工程下的 android 目录,同步更新 Gradle 即可。

dependencies {implementation 'cn.leancloud:storage-android:6.5.11'implementation 'cn.leancloud:realtime-android:6.5.11'implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'}
小 tips: 装置 SDK 期间遇到任何艰难都可在 LeanCloud 社区 发帖求助。

SDK 装置胜利当前,须要别离 初始化 iOS 和 Android 平台。

用户零碎

Demo 外面并没有内置用户零碎,能够抉择联系人列表中的用户名来登录聊天零碎。LeanCloud 即时通信服务端中只须要传入一个惟一标识字符串既能够示意一个「用户」,对应惟一的 Client, 在利用内惟一标识本人的 ID(clientId)。

在本人的我的项目中,如果曾经有独立的用户零碎也很不便保护。
或者应用 LeanStorage 提供的用户零碎。

会话列表

会话列表要展现以后用户所参加的会话,会话名称、会话的成员,会话的最初一条音讯。还须要展现未读音讯数目。

会话列表对应 Conversation 表,查问以后用户的全副会话只须要上面两行代码:

ConversationQuery query = client.conversationQuery();await query.find();

依照会话的更新工夫排序:

query.orderByDescending('updatedAt');

为了展示会话的最新一条音讯,查问的时候要额定加上这行代码:

//让查问后果附带一条最新消息query.includeLastMessage = true;

这样会话页面的后端数据就搞定了。上面看一下如何显示数据。

会话查问胜利返回的数据格式是 Conversation 类型的 List。

  • conversation.name 即会话的名称
  • conversation.members 即会话成员
  • conversation.lastMessage 就是以后会话的最新一条音讯了。

未读音讯数的解决

如果要在 Android 设施上运行,须要在初始化 Java SDK 的时候加上上面这行代码,示意开启未读音讯数告诉:

AVIMOptions.getGlobalOptions().setUnreadNotificationEnabled(true);

swift SDK 是默认反对,无需额定设置。

能够监听 onUnreadMessageCountUpdated 工夫获取未读音讯数告诉:

client.onUnreadMessageCountUpdated = ({  Client client,  Conversation conversation,}) {  // conversation.unreadMessageCount 即该 conversation 的未读音讯数量};

留神要在以下两处革除未读音讯数:

  • 在对话列表点击某对话进入到对话页面时
  • 用户正在某个对话页面聊天,并在这个对话中收到了音讯时

会话详情页面

上拉加载更多历史音讯

查问聊天记录的时候,先查最近的 10 条音讯,而后以第一页的最早的音讯作为开始,持续向前拉取音讯:

List<Message> messages;try {//第一次查问胜利  messages = await conversation.queryMessage(    limit: 10,  );} catch (e) {  print(e);}try {  // 返回的音讯肯定是工夫增序排列,也就是最早的音讯肯定是第一个  Message oldMessage = messages.first;  // 以第一页的最早的音讯作为开始,持续向前拉取音讯  List<Message> messages2 = await conversation.queryMessage(    startTimestamp: oldMessage.sentTimestamp,    startMessageID: oldMessage.id,    startClosed: true,    limit: 10,  );} catch (e) {  print(e);}

批改会话名

对话的名称是会话表 Conversation 默认的属性,更新会话名称只须要执行:

await conversation.updateInfo(attributes: {  'name': 'New Name',});

图片、语音音讯

LeanCloud 即时通信 SDK 提供了上面几种默认的音讯类型,Demo 中只用到了文本音讯,图像音讯和语音音讯。

  • TextMessage 文本音讯
  • ImageMessage 图像音讯
  • AudioMessage 音频音讯
  • VideoMessage 视频音讯
  • FileMessage 一般文件音讯(.txt/.doc/.md 等各种)
  • LocationMessage 地理位置音讯

留神,图片与语音音讯须要先保留成 LCFile,而后再调用发消息接口发消息。

比方发送音频音讯。第一步先保留音频文件为 LCFile:

LCFile file = await LCFile.fromPath('message.wav', path);await file.save();

第二步,再发消息:

//发送音讯AudioMessage audioMessage = AudioMessage.from(  binaryData: file.data,  format: 'wav',);await this.widget.conversation.send(message: audioMessage);

还要留神 iOS 设施发送图片音讯留神关上相册和相机权限,语音音讯须要麦克风权限:

<key>NSMicrophoneUsageDescription</key><string>录音性能须要拜访麦克风,如果不容许,你将无奈在聊天过程中发送语音音讯。</string>   <key>NSCameraUsageDescription</key><string>发送图片性能须要拜访您的相机。如果不容许,你将无奈在聊天过程中发送图片音讯。</string>   <key>NSPhotoLibraryUsageDescription</key><string>发送图片性能须要拜访您的相册。如果不容许,你将无奈在聊天过程中发送相册中的图片。</string>

离线推送告诉

当用户下线当前,收到音讯的时候,往往心愿能有推送揭示。最简略的一种推送设置就是在 LeanCloud 控制台 > 音讯 > 即时通讯 > 设置 > 离线推送设置 页面,填入:

{ "alert": "您有新的音讯", "badge": "Increment" }

这样 iOS 设施有离线音讯的时候会收到揭示。这里 badge 参数为 iOS 设施专用,用于减少利用 badge 上的数字计数。

如果想在 Android 设施上实现离线推送,要减少一步接入 Android 混合推送。

当然在理论我的项目中,离线音讯的揭示往往会要求更加具体,比方推送中要包含音讯的内容或者音讯类型等。LeanCloud 也提供了其余几种定制离线推送的办法,感兴趣能够自行查阅文档。

还要留神,iOS 推送肯定要正确配置 配置 APNs 推送证书,并关上 Xcode 的推送开关:

AppDelegate.swift 中开启推送,要这样设置:

import Flutterimport LeanCloudimport UserNotifications@UIApplicationMain@objc class AppDelegate: FlutterAppDelegate {    override func application(        _ application: UIApplication,        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?    ) -> Bool {        do {            LCApplication.logLevel = .all            try LCApplication.default.set(                 id: "你的APPID",                               key: "你的 APPKey",                               serverURL: "服务器地址")            GeneratedPluginRegistrant.register(with: self)            /*            register APNs to access token, like this:            */            UNUserNotificationCenter.current().getNotificationSettings { (settings) in                switch settings.authorizationStatus {                case .authorized:                    DispatchQueue.main.async {                        UIApplication.shared.registerForRemoteNotifications()                    }                case .notDetermined:                    UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in                        if granted {                            DispatchQueue.main.async {                                UIApplication.shared.registerForRemoteNotifications()                            }                        }                    }                default:                    break                }                _ = LCApplication.default.currentInstallation            }            return super.application(application, didFinishLaunchingWithOptions: launchOptions)        } catch {            fatalError("\(error)")        }    }    override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {        /*        set APNs deviceToken and Team ID.        */        LCApplication.default.currentInstallation.set(            deviceToken: deviceToken,            apnsTeamId: "你的 TeamId")        /*        save to LeanCloud.        */        LCApplication.default.currentInstallation.save { (result) in            switch result {            case .success:                break            case .failure(error: let error):                print(error)            }        }    }    override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {        //如果注册推送失败,能够查看 error 信息        print(error)    }}

投诉举报、黑名单

在 APP Store 审核过程中,因为用户能够随便发送音讯,被要求对用户生成的内容要有适当的预防措施。

要求提供上面这两条内容:

  • A mechanism for users to flag objectionable content

用户标记不良信息的机制,就是举报性能。

  • A mechanism for users to block abusive users

用户阻止滥用用户的机制,理论就是黑名单。

实现举报性能

我的解决办法是在音讯处长按弹出举报窗口。

应用 LeanCloud 存储服务,新建一张 Report 表用于记录举报信息:

//保留一条举报信息LCObject report = LCObject('Report');report['clientID'] = Global.clientID;   //举报人report['messageID'] = messageID;   //音讯 IDreport['conversationID'] = this.widget.conversation.id;  //会话 IDreport['content'] = _selectedReportList.toString(); //举报内容await report.save(); //保留举报信息

能够在控制台查看举报内容:

实现黑名单性能

我的解决办法是,在联系人列表处单击联系人弹框提醒是否退出黑名单。

黑名单实现思路是新建一张 BlackList 表,来保留每个用户的黑名单列表,应用 LeanCloud 云函数 实现屏蔽音讯。在 _messageReceived 这个 Hook 函数下(这个 hook 产生在音讯达到 LeanCloud 云端之后),先查此条音讯的发件人与收件人是否在黑名单列表,如果在黑名单列表就删除其中要求屏蔽的收件人,返回新的收件人列表。

实现起来也比较简单,把上面这个云函数粘贴在 LeanCloud 控制台 > 云引擎 >云函数在线编辑框中即可。

步骤

先点击「创立函数」,而后抉择 _messageReceived 类型,粘贴上面的代码,最初点击「部署」按钮。

期待部署实现,黑名单性能就曾经实现胜利,将不再收到退出黑名单用户的全副音讯。

//上面这个函数粘贴在 LeanCloud 控制台AV.Cloud.define('_messageReceived', async function(request) {let fromPeer = request.params.fromPeer;let toPeersNew = request.params.toPeers;var query = new AV.Query('BlackList');query.equalTo('blackedList', fromPeer);query.containedIn('clientID', toPeersNew);return query.find().then((results) => {    if (results.length > 0) {        var clientID = results[0].get('clientID');        var index = toPeersNew.indexOf(clientID);        if (index > -1) {            toPeersNew.splice(index, 1);        }        return {            toPeers: toPeersNew        }    }    return {    }});})

APP 我的项目地址

APP 我的项目残缺代码见 Github-FlutterRealtimeDemo

APP Store 下载链接

文档

  • LeanCloud 即时通信插件链接
  • LeanCloud 即时通信开发文档
  • Flutter 文档