最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android Flutter基于WebSocket實現(xiàn)即時通訊功能

 更新時間:2022年03月25日 11:55:20   作者:島上碼農(nóng)  
WebSocket是一種在單個TCP連接上進行全雙工通信的協(xié)議。本文將利用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 和當(dāng)前用戶id 一致,則是發(fā)送出去的消息,否則就是接收到的消息。在這里我們因為還沒有用戶體系,先將當(dāng)前的用戶 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 屬性是給消息回復(fù)框使用的。

這里就有一個問題,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)通知該組件刷新了,因此也沒必要再通知了。當(dā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ù)是最新的。其實,相當(dāng)于我們投機取巧實現(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)了,不過實際即時聊天比這個復(fù)雜很多,而且一般也不會用 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)文章

最新評論

漾濞| 娄烦县| 曲沃县| 吴桥县| 张家口市| 进贤县| 祥云县| 宜兰市| 波密县| 青冈县| 清水县| 南投县| 开封县| 顺昌县| 乌拉特后旗| 庄浪县| 孝昌县| 咸阳市| 天镇县| 新兴县| 镇原县| 洛阳市| 汉中市| 班戈县| 汉沽区| 张家港市| 驻马店市| 旬邑县| 辽宁省| 安陆市| 宁阳县| 青龙| 怀仁县| 漠河县| 泽库县| 江北区| 普陀区| 洛扎县| 噶尔县| 河间市| 昌宁县|