使用 Flutter 快速實現聊天應用

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者:隋曉旭"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你是否想過從頭開發一款類似 QQ、微信的聊天應用?又或者,想要在你開發的應用中加入聊天功能,方便用戶交流,增強用戶粘性?那麼,這篇文章就是爲你準備的。在這篇文章中,我將介紹如何基於 Flutter 快速實現一款聊天應用。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"應用簡介"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個基於 Flutter 開發的應用還在持續完善中,現已經支持如下功能:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"登錄、登出"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發起單聊"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發起羣聊"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持文字消息、語音消息、圖片消息"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持展示未讀消息數"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持展示會話成員"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持修改會話名稱"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持離線消息推送"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"投訴舉報不良信息"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"把某用戶加入黑名單"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"頁面截圖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/07/07940e6992c6a1c5301a8be2cee77855.jpeg","alt":"1","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8f/8fe6e8adee40b09a262418f536c86aef.jpeg","alt":"2","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"開發環境搭建"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一步:Flutter 安裝和環境搭建直接查看:"},{"type":"link","attrs":{"href":"https://flutter.dev/docs/get-started/install","title":""},"content":[{"type":"text","text":" Flutter 文檔"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二步:登錄 "},{"type":"link","attrs":{"href":"https://leancloud.cn/dashboard/login.html#/signin","title":""},"content":[{"type":"text","text":"LeanCloud 控制檯"}]},{"type":"text","text":",創建 LeanCloud 應用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在控制檯 > 應用 > 設置 >域名綁定頁面綁定 "},{"type":"text","marks":[{"type":"strong"}],"text":"API 訪問域名"},{"type":"text","text":"。暫時沒有域名可以略過這一步,LeanCloud 也提供了短期有效的免費體驗域名;或者註冊"},{"type":"link","attrs":{"href":"https://console.leancloud.app/login.html#/signin","title":""},"content":[{"type":"text","text":" LeanCloud 國際版"}]},{"type":"text","text":",國際版不要求綁定域名。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在控制檯 > 應用 > 設置 > 應用 Keys 頁面記錄 AppID、AppKey 與服務器地址備用,這裏的服務器地址就是 REST API 服務器地址。如果未綁定域名,控制檯相同的位置可以獲取到免費的共享域名。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"APP 初始化設置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 pubspec.yaml 中,將 LeanCloud Flutter SDK 添加到依賴項列表:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"dependencies:\n leancloud_official_plugin: ^1.0.0-beta.8 //即時通信插件\n leancloud_storage: ^0.2.9 //數據存儲 SDK"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後運行 flutter pub get 安裝 SDK。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲 leancloud"},{"type":"text","marks":[{"type":"italic"}],"text":"official"},{"type":"text","text":"plugin 是基於 "},{"type":"link","attrs":{"href":"https://github.com/leancloud/swift-sdk","title":""},"content":[{"type":"text","text":"Swift SDK"}]},{"type":"text","text":" 以及 [Java Unified SDK](https://github.com/leancloud/java-unified-sdk) 開發,所以還要安裝後面兩個 SDK,這樣應用才能分別在 iOS 和 Android 設備運行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要通過 CocoaPods 安裝 Swift SDK,這一步和安裝 iOS 其他第三方庫是一樣的,在應用的 ios 目錄下執行 pod update 即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"$ cd ios/\n$ pod update # 或者 $ pod install --repo-update"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣的,需要配置 Gradle 來安裝 Java Unified SDK,打開工程目錄 android/app/build.gradle,添加如下依賴,用 Android Studio 打開工程下的 android 目錄,同步更新 Gradle 即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"dependencies {\nimplementation 'cn.leancloud:storage-android:6.5.11'\nimplementation 'cn.leancloud:realtime-android:6.5.11'\nimplementation 'io.reactivex.rxjava2:rxandroid:2.1.1'\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"小 tips: 安裝 SDK 期間遇到任何困難都可在 "},{"type":"link","attrs":{"href":"https://forum.leancloud.cn/latest","title":""},"content":[{"type":"text","text":"LeanCloud 社區"}]},{"type":"text","text":" 發帖求助。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SDK 安裝成功以後,需要分別 "},{"type":"link","attrs":{"href":"https://leancloud.cn/docs/sdk_setup-flutter.html#hash662673194","title":""},"content":[{"type":"text","text":"初始化 iOS 和 Android 平臺"}]},{"type":"text","text":"。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"用戶系統"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Demo 裏面並沒有內置用戶系統,可以選擇聯繫人列表中的用戶名來登錄聊天系統。LeanCloud 即時通信服務端中只需要傳入一個唯一標識字符串既可以表示一個「用戶」,對應唯一的 Client, 在應用內唯一標識自己的 ID(clientId)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在自己的項目中,如果已經有獨立的用戶系統也很方便維護。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"或者使用 LeanStorage 提供的"},{"type":"link","attrs":{"href":"https://leancloud.cn/docs/leanstorage_guide-java.html#hash954895","title":""},"content":[{"type":"text","text":"用戶系統"}]},{"type":"text","text":"。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"會話列表"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"會話列表要展示當前用戶所參與的會話,會話名稱、會話的成員,會話的最後一條消息。還需要展示未讀消息數目。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"會話列表對應 Conversation 表,查詢當前用戶的全部會話只需要下面兩行代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"ConversationQuery query = client.conversationQuery();\nawait query.find();"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"按照會話的更新時間排序:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"query.orderByDescending('updatedAt');"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了展示會話的最新一條消息,查詢的時候要額外加上這行代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"//讓查詢結果附帶一條最新消息\nquery.includeLastMessage = true;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣會話頁面的後端數據就搞定了。下面看一下如何顯示數據。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"會話查詢成功返回的數據格式是 Conversation 類型的 List。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"conversation.name 即會話的名稱"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"conversation.members 即會話成員"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"conversation.lastMessage 就是當前會話的最新一條消息了。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"未讀消息數的處理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果要在 Android 設備上運行,需要在初始化 Java SDK 的時候加上下面這行代碼,表示開啓未讀消息數通知:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"AVIMOptions.getGlobalOptions().setUnreadNotificationEnabled(true);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"swift SDK 是默認支持,無需額外設置。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以監聽 onUnreadMessageCountUpdated 時間獲取未讀消息數通知:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"client.onUnreadMessageCountUpdated = ({\n Client client,\n Conversation conversation,\n}) {\n // conversation.unreadMessageCount 即該 conversation 的未讀消息數量\n};"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意要在以下兩處清除未讀消息數:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在對話列表點擊某對話進入到對話頁面時"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶正在某個對話頁面聊天,並在這個對話中收到了消息時"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"會話詳情頁面"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"上拉加載更多歷史消息"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"查詢聊天記錄的時候,先查最近的 10 條消息,然後以第一頁的最早的消息作爲開始,繼續向前拉取消息:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"List messages;\ntry {\n//第一次查詢成功\n messages = await conversation.queryMessage(\n limit: 10,\n );\n} catch (e) {\n print(e);\n}\n\ntry {\n // 返回的消息一定是時間增序排列,也就是最早的消息一定是第一個\n Message oldMessage = messages.first;\n // 以第一頁的最早的消息作爲開始,繼續向前拉取消息\n List messages2 = await conversation.queryMessage(\n startTimestamp: oldMessage.sentTimestamp,\n startMessageID: oldMessage.id,\n startClosed: true,\n limit: 10,\n );\n} catch (e) {\n print(e);\n}"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"修改會話名"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對話的名稱是會話表 Conversation 默認的屬性,更新會話名稱只需要執行:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"await conversation.updateInfo(attributes: {\n 'name': 'New Name',\n});"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"圖片、語音消息"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LeanCloud 即時通信 SDK 提供了下面幾種默認的消息類型,Demo 中只用到了文本消息,圖像消息和語音消息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TextMessage 文本消息"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ImageMessage 圖像消息"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AudioMessage 音頻消息"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"VideoMessage 視頻消息"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FileMessage 普通文件消息(.txt/.doc/.md 等各種)"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LocationMessage 地理位置消息"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"注意,圖片與語音消息需要先保存成 LCFile,然後再調用發消息接口發消息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如發送音頻消息。第一步先保存音頻文件爲 LCFile:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"LCFile file = await LCFile.fromPath('message.wav', path);\nawait file.save();"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二步,再發消息:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"//發送消息\nAudioMessage audioMessage = AudioMessage.from(\n binaryData: file.data,\n format: 'wav',\n);\nawait this.widget.conversation.send(message: audioMessage);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還要注意 iOS 設備發送圖片消息注意打開相冊和相機權限,語音消息需要麥克風權限:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"NSMicrophoneUsageDescription\n錄音功能需要訪問麥克風,如果不允許,你將無法在聊天過程中發送語音消息。\n \nNSCameraUsageDescription\n發送圖片功能需要訪問您的相機。如果不允許,你將無法在聊天過程中發送圖片消息。\n \nNSPhotoLibraryUsageDescription\n發送圖片功能需要訪問您的相冊。如果不允許,你將無法在聊天過程中發送相冊中的圖片。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"離線推送通知"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當用戶下線以後,收到消息的時候,往往希望能有推送提醒。最簡單的一種推送設置就是在 LeanCloud "},{"type":"text","marks":[{"type":"strong"}],"text":"控制檯 > 消息 > 即時通訊 > 設置 > 離線推送設置"},{"type":"text","text":" 頁面,填入:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"{ \"alert\": \"您有新的消息\", \"badge\": \"Increment\" }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣 iOS 設備有離線消息的時候會收到提醒。這裏 badge 參數爲 iOS 設備專用,用於增加應用 badge 上的數字計數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果想在 Android 設備上實現離線推送,要增加一步接入"},{"type":"link","attrs":{"href":"https://leancloud.cn/docs/androidmixpushguide.html","title":""},"content":[{"type":"text","text":" Android 混合推送"}]},{"type":"text","text":"。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然在實際項目中,離線消息的提醒往往會要求更加具體,比如推送中要包括消息的內容或者消息類型等。LeanCloud 也提供了其他幾種定製離線推送的方法,感興趣可以自行查閱"},{"type":"link","attrs":{"href":"https://leancloud.cn/docs/realtime-guide-intermediate.html#hash-485620600","title":""},"content":[{"type":"text","text":"文檔"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還要注意,iOS 推送一定要正確配置 "},{"type":"link","attrs":{"href":"https://leancloud.cn/docs/iospushcert.html","title":""},"content":[{"type":"text","text":"配置 APNs 推送證書"}]},{"type":"text","text":",並打開 Xcode 的推送開關:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0d/0d808a8b7322824201e28215b2ee96e3.png","alt":"7","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AppDelegate.swift 中開啓推送,要這樣設置:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"import Flutter\nimport LeanCloud\nimport UserNotifications\n\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate {\n override func application(\n _ application: UIApplication,\n didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n ) -> Bool {\n do {\n LCApplication.logLevel = .all\n try LCApplication.default.set(\n id: \"你的APPID\",\n key: \"你的 APPKey\",\n serverURL: \"服務器地址\")\n GeneratedPluginRegistrant.register(with: self)\n /*\n register APNs to access token, like this:\n */\n UNUserNotificationCenter.current().getNotificationSettings { (settings) in\n switch settings.authorizationStatus {\n case .authorized:\n DispatchQueue.main.async {\n UIApplication.shared.registerForRemoteNotifications()\n }\n case .notDetermined:\n UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in\n if granted {\n DispatchQueue.main.async {\n UIApplication.shared.registerForRemoteNotifications()\n }\n }\n }\n default:\n break\n }\n _ = LCApplication.default.currentInstallation\n }\n return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n } catch {\n fatalError(\"\\(error)\")\n }\n }\n\n override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {\n /*\n set APNs deviceToken and Team ID.\n */\n LCApplication.default.currentInstallation.set(\n deviceToken: deviceToken,\n apnsTeamId: \"你的 TeamId\")\n /*\n save to LeanCloud.\n */\n LCApplication.default.currentInstallation.save { (result) in\n switch result {\n case .success:\n break\n case .failure(error: let error):\n print(error)\n }\n }\n }\n override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {\n //如果註冊推送失敗,可以檢查 error 信息\n print(error)\n }\n}"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"投訴舉報、黑名單"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 APP Store 審覈過程中,因爲用戶可以隨意發送消息,被要求對用戶生成的內容要有適當的預防措施。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要求提供下面這兩條內容:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"A mechanism for users to flag objectionable content "}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶標記不良信息的機制,就是舉報功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"A mechanism for users to block abusive users"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶阻止濫用用戶的機制,實際就是黑名單。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"實現舉報功能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我的解決辦法是在消息處長按彈出舉報窗口。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/67/673c912f4d871440f930ead8916c09e8.png","alt":"8","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 LeanCloud 存儲服務,新建一張 Report 表用於記錄舉報信息:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"//保存一條舉報信息\nLCObject report = LCObject('Report');\nreport['clientID'] = Global.clientID; //舉報人\nreport['messageID'] = messageID; //消息 ID\nreport['conversationID'] = this.widget.conversation.id; //會話 ID\nreport['content'] = _selectedReportList.toString(); //舉報內容\nawait report.save(); //保存舉報信息"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以在控制檯查看舉報內容:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b8/b8712307aa63fb0fe6b5f52458eede8e.png","alt":"9","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"實現黑名單功能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8a/8a7daa7e3ec675860f1ab7c3a0bda75c.png","alt":"10","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我的解決辦法是,在聯繫人列表處單擊聯繫人彈框提示是否加入黑名單。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"黑名單實現思路是新建一張 BlackList 表,來保存每個用戶的黑名單列表,使用 "},{"type":"link","attrs":{"href":"https://leancloud.cn/docs/realtime-guide-systemconv.html#hash1748033991","title":""},"content":[{"type":"text","text":"LeanCloud 雲函數"}]},{"type":"text","text":" 實現屏蔽消息。在 [_messageReceived](https://leancloud.cn/docs/realtime-guide-systemconv.html#hash-1573260517) 這個 Hook 函數下(這個 hook 發生在消息到達 LeanCloud 雲端之後),先查此條消息的發件人與收件人是否在黑名單列表,如果在黑名單列表就刪除其中要求屏蔽的收件人,返回新的收件人列表。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實現起來也比較簡單,把下面這個雲函數粘貼在 LeanCloud 控制檯 > 雲引擎 >雲函數在線編輯框中即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1f/1f76a7b79e7ee200d1cabb3a344309f4.png","alt":"11","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"步驟"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先點擊「創建函數」,然後選擇 _messageReceived 類型,粘貼下面的代碼,最後點擊「部署」按鈕。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"等待部署完成,黑名單功能就已經實現成功,將不再收到加入黑名單用戶的全部消息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"//下面這個函數粘貼在 LeanCloud 控制檯\n\nAV.Cloud.define('_messageReceived', async function(request) {\nlet fromPeer = request.params.fromPeer;\nlet toPeersNew = request.params.toPeers;\n\nvar query = new AV.Query('BlackList');\nquery.equalTo('blackedList', fromPeer);\nquery.containedIn('clientID', toPeersNew);\nreturn query.find().then((results) => {\n if (results.length > 0) {\n var clientID = results[0].get('clientID');\n var index = toPeersNew.indexOf(clientID);\n if (index > -1) {\n toPeersNew.splice(index, 1);\n }\n return {\n toPeers: toPeersNew\n }\n }\n return {\n }\n});\n})"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"APP 項目地址"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"APP 項目完整代碼見 "},{"type":"link","attrs":{"href":"https://github.com/SXiaoXu/FlutterRealtimeDemo","title":""},"content":[{"type":"text","text":"Github-FlutterRealtimeDemo"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://apps.apple.com/cn/app/leanmessage/id1529417244","title":""},"content":[{"type":"text","text":"APP Store 下載鏈接"}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"文檔"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://pub.dev/packages/leancloudofficialplugin#leancloudofficialplugin","title":""},"content":[{"type":"text","text":"LeanCloud 即時通信插件鏈接"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://leancloud.cn/docs/#即時通訊","title":""},"content":[{"type":"text","text":"LeanCloud 即時通信開發文檔"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://flutter.dev/docs","title":""},"content":[{"type":"text","text":"Flutter 文檔"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章