使用 Flutter 快速實現聊天應用

做者:隋曉旭html

你是否想過從頭開發一款相似 QQ、微信的聊天應用?又或者,想要在你開發的應用中加入聊天功能,方便用戶交流,加強用戶粘性?那麼,這篇文章就是爲你準備的。在這篇文章中,我將介紹如何基於 Flutter 快速實現一款聊天應用。java

應用簡介

這個基於 Flutter 開發的應用還在持續完善中,現已經支持以下功能:react

  • 登陸、登出
  • 發起單聊
  • 發起羣聊
  • 支持文字消息、語音消息、圖片消息
  • 支持展現未讀消息數
  • 支持展現會話成員
  • 支持修改會話名稱
  • 支持離線消息推送
  • 投訴舉報不良信息
  • 把某用戶加入黑名單

頁面截圖

1

2

開發環境搭建

第一步:Flutter 安裝和環境搭建直接查看: Flutter 文檔
第二步:登陸 LeanCloud 控制檯,建立 LeanCloud 應用。android

  • 在控制檯 > 應用 > 設置 >域名綁定頁面綁定 API 訪問域名。暫時沒有域名能夠略過這一步,LeanCloud 也提供了短時間有效的免費體驗域名;或者註冊 LeanCloud 國際版,國際版不要求綁定域名。
  • 在控制檯 > 應用 > 設置 > 應用 Keys 頁面記錄 AppID、AppKey 與服務器地址備用,這裏的服務器地址就是 REST API 服務器地址。若是未綁定域名,控制檯相同的位置能夠獲取到免費的共享域名。

APP 初始化設置

在 pubspec.yaml 中,將 LeanCloud Flutter SDK 添加到依賴項列表:ios

dependencies:
  leancloud_official_plugin: ^1.0.0-beta.8   //即時通訊插件
  leancloud_storage: ^0.2.9  //數據存儲 SDK

而後運行 flutter pub get 安裝 SDK。git

由於 leancloud_official_plugin 是基於 Swift SDK 以及 Java Unified SDK 開發,因此還要安裝後面兩個 SDK,這樣應用才能分別在 iOS 和 Android 設備運行。github

須要經過 CocoaPods 安裝 Swift SDK,這一步和安裝 iOS 其餘第三方庫是同樣的,在應用的 ios 目錄下執行 pod update 便可。swift

$ 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 的推送開關:

7

AppDelegate.swift 中開啓推送,要這樣設置:

import Flutter
import LeanCloud
import 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

用戶阻止濫用用戶的機制,實際就是黑名單。

實現舉報功能

個人解決辦法是在消息處長按彈出舉報窗口。

8

使用 LeanCloud 存儲服務,新建一張 Report 表用於記錄舉報信息:

//保存一條舉報信息
LCObject report = LCObject('Report');
report['clientID'] = Global.clientID;   //舉報人
report['messageID'] = messageID;   //消息 ID
report['conversationID'] = this.widget.conversation.id;  //會話 ID
report['content'] = _selectedReportList.toString(); //舉報內容
await report.save(); //保存舉報信息

能夠在控制檯查看舉報內容:

9

實現黑名單功能

10

個人解決辦法是,在聯繫人列表處單擊聯繫人彈框提示是否加入黑名單。

黑名單實現思路是新建一張 BlackList 表,來保存每一個用戶的黑名單列表,使用 LeanCloud 雲函數 實現屏蔽消息。在 _messageReceived 這個 Hook 函數下(這個 hook 發生在消息到達 LeanCloud 雲端以後),先查此條消息的發件人與收件人是否在黑名單列表,若是在黑名單列表就刪除其中要求屏蔽的收件人,返回新的收件人列表。

實現起來也比較簡單,把下面這個雲函數粘貼在 LeanCloud 控制檯 > 雲引擎 >雲函數在線編輯框中便可。

11

步驟

先點擊「建立函數」,而後選擇 _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 下載連接

文檔

相關文章
相關標籤/搜索