千家信息网

Android Flutter基于WebSocket怎么实现即时通讯功能

发表于:2024-09-21 作者:千家信息网编辑
千家信息网最后更新 2024年09月21日,这篇文章主要介绍"Android Flutter基于WebSocket怎么实现即时通讯功能",在日常操作中,相信很多人在Android Flutter基于WebSocket怎么实现即时通讯功能问题上存
千家信息网最后更新 2024年09月21日Android Flutter基于WebSocket怎么实现即时通讯功能

这篇文章主要介绍"Android Flutter基于WebSocket怎么实现即时通讯功能",在日常操作中,相信很多人在Android Flutter基于WebSocket怎么实现即时通讯功能问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"Android Flutter基于WebSocket怎么实现即时通讯功能"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

联系人界面构建

我们在聊天前,需要选择对应的联系人进行单聊,因此需要构建一个联系人列表。这里我们使用简单的 ListView.builder+Mock 数据构建联系人列表。界面如下所示,其中关键的就是点击联系人时将联系人的 id通过路由传递过去,以便发送消息时通过用户 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}');  },);

聊天界面的实现

我们将发送的消息放在右边,将接收到的消息放在左边,居左还是居右通过 Containermargin 来实现。至于区分,通过消息对象的fromUserId 来区分,如果 fromUserId 和当前用户id 一致,则是发送出去的消息,否则就是接收到的消息。在这里我们因为还没有用户体系,先将当前的用户 id 写死。为了实现模拟器之间的聊天,我们一个模拟器设置为 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 推送的最新的消息,为了统一,我们将接收消息和发送消息都通过StreamProvider推送更新界面。

// 发送消息时将消息加入到流控制器中void sendMessage(String event, T message) {  _socket.emit(event, message);  _socketResponse.sink.add(message);}// 接收消息时也加入到流控制器中_socket.on(recvEvent, (data) {  _socketResponse.sink.add(data);});

这样不管是接收消息还是发送消息都会通过 StreamProvider 重新构建聊天界面。那问题来了,聊天列表数据如何刷新呢?

消息界面的 MultiProvider

消息界面需要接收 StreamProvider 的消息流,还需要使用消息列表数据,这里我们使用了 MultiProvider。其中消息发送框和聊天界面共用 ChatMessageModel(仅为演示,实际可以拆分开)。

final chatMessageModel = ChatMessageModel();//...body: Stack(  alignment: Alignment.bottomCenter,  children: [    MultiProvider(      providers: [        StreamProvider?>(            create: (context) => streamSocket.getResponse,            initialData: null),        ChangeNotifierProvider.value(value: chatMessageModel)      ],      child: StreamDemo(),    ),    ChangeNotifierProvider.value(      child: MessageReplyBar(messageSendHandler: (message) {        Map json = {          'fromUserId': 'user1',          'toUserId': widget.toUserId,          'contentType': 'text',          'content': message        };        streamSocket.sendMessage('chat', json);      }),      value: chatMessageModel,    ),]//...

其中ChatMessageModel即消息列表状态数据,里面只有一个消息对象数组和一个添加消息方法,以及一个 content 属性是给消息回复框使用的。

这里就有一个问题,StreamProvider 推送 StreamSocket过来的消息的时候, ChatMessageModel 其实是不知道的。如果要知道,一个办法就是在 StreamSocket 引用 ChatMessageModel对象,然后调用其 addMessage 方法添加消息。但是这样会增加两个类的耦合。还有一种方式是取巧的方式了,那就是 StreamdDemobuild 方法能够获取到 StreamSocket 推送的最新消息,在这里读取到最新的消息后就可以添加到消息列表了。由于前面我们发送消息和接收消息都将消息加入到了消息流中,这样处理方式就统一了。

这种方式需要注意,Provider 不允许在组件的build 方法中再次调用类似 notifyListeners 的方法通知该组件刷新,因此在 ChatMessageModeladdMessage 方法里不可以使用notifyListeners来通知组件刷新,否则会出现同一组件刷新冲突。实际上,因为另一个Provider 已经通知该组件刷新了,因此也没必要再通知了。当然,这仅仅是一种取巧方法,假设这个addMessage 方法还需要通知其他组件刷新,那这种形式就就不可取了。

class ChatMessageModel with ChangeNotifier {  List _messages = [];  List get messages => _messages;  String content = '';  void addMessage(Map json) {    _messages.add(MessageEntity.fromJson(json));  }}

这里我们先不考虑这种情况,StreamDemobuild关于这部分的处理方法如下,这里对于吧 ChatMessageModel 也就不需要使用 watch 方法了,完全依赖于 StreamProvider 的流推送来更新组件。每次发送消息或接收消息后,构建时在返回组件树前就更新了消息列表数据了,因此也能保证数据是最新的。其实,相当于我们投机取巧实现了两个 Provider之间的数据交互。

@overrideWidget build(BuildContext context) {  Map? messageJson = context.watch?>();  if (messageJson != null) {    context.read().addMessage(messageJson);  }  List messages = context.read().messages;  // ListView 部分}

运行效果

来看一下运行效果,模拟器的好处就是可以开多个调试。效果是实现了,不过实际即时聊天比这个复杂很多,而且一般也不会用 Socket,但是如果 App 内部要实现应用打开后的即时消息推送,WebSocket 是一个不错的选择。源码已经提交,后端和Flutter 代码分布如下:

Flutter Provider 部分代码(null safety 版本)

后端代码(Express 版本)

到此,关于"Android Flutter基于WebSocket怎么实现即时通讯功能"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0