Android Flutter基于WebSocket實現(xiàn)即時通訊功能
前言
我們在前面花了很大篇幅介紹 Provider 狀態(tài)管理,這是因為在 Flutter 中,Provider 是眾多狀態(tài)管理插件的首選。本篇以即時聊天為例,來講述 Provider 的綜合應用,也算是 Provider 狀態(tài)管理系列的終結篇。本篇涉及的內(nèi)容如下:
- 聯(lián)系人界面的構建;
- 聊天界面的簡單實現(xiàn);
- StreamProvider 接收 Socket流數(shù)據(jù)并自動通知界面刷新;
- MultiProvider為聊天主界面提供多個Provider狀態(tài);
- 多個 Provider存在交叉數(shù)據(jù)時處理方式。
聯(lián)系人界面構建
我們在聊天前,需要選擇對應的聯(lián)系人進行單聊,因此需要構建一個聯(lián)系人列表。這里我們使用簡單的 ListView.builder+Mock 數(shù)據(jù)構建聯(liá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)模擬器之間的聊天,我們一個模擬器設置為 user1,一個設置為 user2。界面也是使用ListView.builder(萬能不?)構建。

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 重新構建聊天界面。那問題來了,聊天列表數(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)通知該組件刷新了,因此也沒必要再通知了。當然,這僅僅是一種取巧方法,假設這個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關于這部分的處理方法如下,這里對于吧 ChatMessageModel 也就不需要使用 watch 方法了,完全依賴于 StreamProvider 的流推送來更新組件。每次發(fā)送消息或接收消息后,構建時在返回組件樹前就更新了消息列表數(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)應用打開后的即時消息推送,WebSocket 是一個不錯的選擇。源碼已經(jīng)提交,后端和Flutter 代碼分布如下:
Flutter Provider 部分代碼(null safety 版本)
后端代碼(Express 版本)

以上就是Android Flutter基于WebSocket實現(xiàn)即時通訊功能的詳細內(nèi)容,更多關于Flutter WebSocket通訊的資料請關注腳本之家其它相關文章!
相關文章
Android中通過AsyncTask類來制作炫酷進度條的實例教程
這篇文章主要介紹了Android中通過AsyncTask來制作炫酷進度條的實例教程,借助AsyncTask類的線程操作方法來管理異步任務,需要的朋友可以參考下2016-05-05
Android RelativeLayout相對布局屬性簡析
在Android應用開發(fā)過程中,為了界面的美觀考慮,經(jīng)常會使用到布局方面的屬性,本文就以此問題深入解析,詳解一下Android RelativeLayout相對布局屬性在實際開發(fā)中的應用,需要的朋友可以參考下2012-11-11
Android應用程序窗口(Activity)窗口對象(Window)創(chuàng)建指南
本文將詳細介紹Android應用程序窗口(Activity)的窗口對象(Window)的創(chuàng)建過程,需要了解的朋友可以參考下2012-12-12
解決Android應用冷啟動時出現(xiàn)的白屏問題的方法
本篇文章主要介紹了解決Android應用冷啟動時出現(xiàn)的白屏問題的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
Android+OpenCV4.2.0環(huán)境配置詳解(Android studio)
這篇文章主要介紹了Android+OpenCV4.2.0環(huán)境配置詳解(Android studio),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-10-10

