Android Flutter基于WebSocket實現(xiàn)即時通訊功能
前言
我們在前面花了很大篇幅介紹 Provider
狀態(tài)管理,這是因為在 Flutter 中,Provider
是眾多狀態(tài)管理插件的首選。本篇以即時聊天為例,來講述 Provider
的綜合應(yīng)用,也算是 Provider
狀態(tài)管理系列的終結(jié)篇。本篇涉及的內(nèi)容如下:
- 聯(lián)系人界面的構(gòu)建;
- 聊天界面的簡單實現(xiàn);
- StreamProvider 接收 Socket流數(shù)據(jù)并自動通知界面刷新;
- MultiProvider為聊天主界面提供多個Provider狀態(tài);
- 多個 Provider存在交叉數(shù)據(jù)時處理方式。
聯(lián)系人界面構(gòu)建
我們在聊天前,需要選擇對應(yīng)的聯(lián)系人進行單聊,因此需要構(gòu)建一個聯(lián)系人列表。這里我們使用簡單的 ListView.builder+Mock 數(shù)據(jù)構(gòu)建聯(lián)系人列表。界面如下所示,其中關(guān)鍵的就是點擊聯(lián)系人時將聯(lián)系人的 id通過路由傳遞過去,以便發(fā)送消息時通過用戶 id指定接收用戶。
return?ListTile( ??leading:?_getRoundImage(contactors[index].avatar,?50), ??title:?Text(contactors[index].nickname), ??subtitle:?Text( ????contactors[index].description, ????style:?TextStyle(fontSize:?14.0,?color:?Colors.grey), ??), ??onTap:?()?{ ????debugPrint(contactors[index].id); ????RouterManager.router.navigateTo(context, ????????'${RouterManager.chatPath}?toUserId=${contactors[index].id}'); ??}, );
聊天界面的實現(xiàn)
我們將發(fā)送的消息放在右邊,將接收到的消息放在左邊,居左還是居右通過 Container
的 margin
來實現(xiàn)。至于區(qū)分,通過消息對象的fromUserId
來區(qū)分,如果 fromUserId
和當前用戶id
一致,則是發(fā)送出去的消息,否則就是接收到的消息。在這里我們因為還沒有用戶體系,先將當前的用戶 id
寫死。為了實現(xiàn)模擬器之間的聊天,我們一個模擬器設(shè)置為 user1
,一個設(shè)置為 user2
。界面也是使用ListView.builder
(萬能不?)構(gòu)建。
return?ListView.builder( ??itemBuilder:?(context,?index)?{ ????MessageEntity?message?=?messages[index]; ????double?margin?=?20; ????double?marginLeft?=?message.fromUserId?==?'user1'???60?:?margin; ????double?marginRight?=?message.fromUserId?==?'user1'???margin?:?60; ????return?Container( ??????margin:?EdgeInsets.fromLTRB(marginLeft,?margin,?marginRight,?margin), ??????padding:?EdgeInsets.all(15), ??????alignment:?message.fromUserId?==?'user1' ????????????Alignment.centerRight ??????????:?Alignment.centerLeft, ??????decoration:?BoxDecoration( ??????????color:?message.fromUserId?==?'user1' ????????????????Colors.green[300] ??????????????:?Colors.blue[400], ??????????borderRadius:?BorderRadius.circular(10)), ??????child:?Text( ????????message.content, ????????style:?TextStyle(color:?Colors.white), ??????), ????); ??}, ??itemCount:?messages.length, );
聊天界面的一個特點是會接收StreamProvider
推送的最新的消息,為了統(tǒng)一,我們將接收消息和發(fā)送消息都通過StreamProvider
推送更新界面。
//?發(fā)送消息時將消息加入到流控制器中 void?sendMessage(String?event,?T?message)?{ ??_socket.emit(event,?message); ??_socketResponse.sink.add(message); } //?接收消息時也加入到流控制器中 _socket.on(recvEvent,?(data)?{ ??_socketResponse.sink.add(data); });
這樣不管是接收消息還是發(fā)送消息都會通過 StreamProvider
重新構(gòu)建聊天界面。那問題來了,聊天列表數(shù)據(jù)如何刷新呢?
消息界面的 MultiProvider
消息界面需要接收 StreamProvider
的消息流,還需要使用消息列表數(shù)據(jù),這里我們使用了 MultiProvider
。其中消息發(fā)送框和聊天界面共用 ChatMessageModel
(僅為演示,實際可以拆分開)。
final?chatMessageModel?=?ChatMessageModel(); //... body:?Stack( ??alignment:?Alignment.bottomCenter, ??children:?[ ????MultiProvider( ??????providers:?[ ????????StreamProvider<Map<String,?dynamic>?>( ????????????create:?(context)?=>?streamSocket.getResponse, ????????????initialData:?null), ????????ChangeNotifierProvider.value(value:?chatMessageModel) ??????], ??????child:?StreamDemo(), ????), ????ChangeNotifierProvider.value( ??????child:?MessageReplyBar(messageSendHandler:?(message)?{ ????????Map<String,?String>?json?=?{ ??????????'fromUserId':?'user1', ??????????'toUserId':?widget.toUserId, ??????????'contentType':?'text', ??????????'content':?message ????????}; ????????streamSocket.sendMessage('chat',?json); ??????}), ??????value:?chatMessageModel, ????), ] //...
其中ChatMessageModel
即消息列表狀態(tài)數(shù)據(jù),里面只有一個消息對象數(shù)組和一個添加消息方法,以及一個 content
屬性是給消息回復框使用的。
這里就有一個問題,StreamProvider
推送 StreamSocket
過來的消息的時候, ChatMessageModel
其實是不知道的。如果要知道,一個辦法就是在 StreamSocket
引用 ChatMessageModel
對象,然后調(diào)用其 addMessage
方法添加消息。但是這樣會增加兩個類的耦合。還有一種方式是取巧的方式了,那就是 StreamdDemo
的 build
方法能夠獲取到 StreamSocket
推送的最新消息,在這里讀取到最新的消息后就可以添加到消息列表了。由于前面我們發(fā)送消息和接收消息都將消息加入到了消息流中,這樣處理方式就統(tǒng)一了。
這種方式需要注意,Provider
不允許在組件的build
方法中再次調(diào)用類似 notifyListeners
的方法通知該組件刷新,因此在 ChatMessageModel
的 addMessage
方法里不可以使用notifyListeners
來通知組件刷新,否則會出現(xiàn)同一組件刷新沖突。實際上,因為另一個Provider
已經(jīng)通知該組件刷新了,因此也沒必要再通知了。當然,這僅僅是一種取巧方法,假設(shè)這個addMessage
方法還需要通知其他組件刷新,那這種形式就就不可取了。
class?ChatMessageModel?with?ChangeNotifier?{ ??List<MessageEntity>?_messages?=?[]; ??List<MessageEntity>?get?messages?=>?_messages; ??String?content?=?''; ??void?addMessage(Map<String,?dynamic>?json)?{ ????_messages.add(MessageEntity.fromJson(json)); ??} }
這里我們先不考慮這種情況,StreamDemo
的 build
關(guān)于這部分的處理方法如下,這里對于吧 ChatMessageModel
也就不需要使用 watch
方法了,完全依賴于 StreamProvider
的流推送來更新組件。每次發(fā)送消息或接收消息后,構(gòu)建時在返回組件樹前就更新了消息列表數(shù)據(jù)了,因此也能保證數(shù)據(jù)是最新的。其實,相當于我們投機取巧實現(xiàn)了兩個 Provider
之間的數(shù)據(jù)交互。
@override Widget?build(BuildContext?context)?{ ??Map<String,?dynamic>??messageJson?=?context.watch<Map<String,?dynamic>?>(); ??if?(messageJson?!=?null)?{ ????context.read<ChatMessageModel>().addMessage(messageJson); ??} ??List<MessageEntity>?messages?=?context.read<ChatMessageModel>().messages; ??//?ListView?部分 }
運行效果
來看一下運行效果,模擬器的好處就是可以開多個調(diào)試。效果是實現(xiàn)了,不過實際即時聊天比這個復雜很多,而且一般也不會用 Socket
,但是如果 App 內(nèi)部要實現(xiàn)應(yīng)用打開后的即時消息推送,WebSocket
是一個不錯的選擇。源碼已經(jīng)提交,后端和Flutter 代碼分布如下:
Flutter Provider 部分代碼(null safety 版本)
后端代碼(Express 版本)
以上就是Android Flutter基于WebSocket實現(xiàn)即時通訊功能的詳細內(nèi)容,更多關(guān)于Flutter WebSocket通訊的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決Android 10/Android Q手機在后臺無法正常定位問題
這篇文章主要介紹了解決Android 10/Android Q手機在后臺無法正常定位問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-11-11Android實現(xiàn)長按圓環(huán)動畫View效果的思路代碼
這篇文章主要介紹了Android實現(xiàn)長按圓環(huán)動畫View效果,本文給大家分享實現(xiàn)思路,通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09FFmpeg?Principle學習new_video_stream添加視頻輸出流
這篇文章主要為大家介紹了FFmpeg?Principle學習new_video_stream添加視頻輸出流示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10Android 調(diào)用系統(tǒng)相冊選擇照片
這篇文章主要介紹了Android 調(diào)用系統(tǒng)相冊選擇照片的方法,幫助大家更好的進行Android開發(fā),感興趣的朋友可以了解下2020-12-12Android 獲取內(nèi)外SD卡路徑幾種方法總結(jié)
這篇文章主要介紹了Android 獲得內(nèi)外SD卡路徑幾種方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2016-12-12Android實現(xiàn)GridView中的item自由拖動效果
在前一個項目中,實現(xiàn)了一個功能是gridview中的item自由拖到效果,實現(xiàn)思路很簡單,主要工作就是交換節(jié)點,以及拖動時的移動效果,下面小編給大家分享具體實現(xiàn)過程,對gridview實現(xiàn)拖拽效果感興趣的朋友一起看看吧2016-11-11Android通過startService實現(xiàn)文件批量下載
這篇文章主要為大家詳細介紹了Android通過startService實現(xiàn)文件批量下載的示例,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2015-12-12