在这里插入图片描述

前言

即时通讯是OA系统中促进团队协作的核心功能,它支持员工之间的实时沟通、文件共享、群组讨论等。一个优秀的聊天组件需要支持多种消息类型、消息状态管理、历史记录加载等功能。本文将详细介绍如何使用Flutter和OpenHarmony开发一个功能完善的即时通讯聊天组件,提升企业内部沟通效率。

组件功能设计

聊天组件的核心是消息列表和输入区域。消息列表需要支持文本、图片、文件、语音等多种消息类型,显示发送时间、已读状态等信息。输入区域支持文本输入、表情选择、附件上传等功能。组件还需要支持下拉加载历史消息、新消息提醒、消息搜索等功能。

Flutter端实现

定义消息类型和数据模型:

enum MessageType { text, image, file, voice, video, location }
enum MessageStatus { sending, sent, delivered, read, failed }

class ChatMessage {
  final String id;
  final String senderId;
  final String senderName;
  final String senderAvatar;
  final MessageType type;
  final String content;
  final DateTime time;
  final MessageStatus status;
  final Map<String, dynamic>? extra;
  
  ChatMessage({
    required this.id,
    required this.senderId,
    required this.senderName,
    required this.senderAvatar,
    required this.type,
    required this.content,
    required this.time,
    this.status = MessageStatus.sending,
    this.extra,
  });
  
  bool get isMine => senderId == currentUserId;
}

ChatMessage模型包含消息的完整信息,extra字段存储不同类型消息的扩展数据,如图片的宽高、文件的大小等。isMine计算属性判断是否为自己发送的消息,用于决定消息的显示位置。

聊天组件的基础结构:

class ChatWidget extends StatefulWidget {
  final List<ChatMessage> messages;
  final Function(String) onSendText;
  final Function(String) onSendImage;
  
  const ChatWidget({
    Key? key,
    required this.messages,
    required this.onSendText,
    required this.onSendImage,
  }) : super(key: key);
  
  
  State<ChatWidget> createState() => _ChatWidgetState();
}

组件接收消息列表和发送回调函数,onSendText处理文本消息,onSendImage处理图片消息。这种设计使组件专注于UI展示,消息发送逻辑由父组件处理。

消息列表构建:

Widget _buildMessageList() {
  return ListView.builder(
    controller: _scrollController,
    reverse: true,
    itemCount: widget.messages.length,
    itemBuilder: (context, index) {
      final message = widget.messages[widget.messages.length - 1 - index];
      return _buildMessageItem(message);
    },
  );
}

消息列表使用reverse模式,新消息显示在底部。ListView.builder实现懒加载,只渲染可见区域的消息,适合大量消息的场景。

消息气泡构建:

Widget _buildMessageItem(ChatMessage message) {
  final isMine = message.isMine;
  
  return Padding(
    padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
    child: Row(
      mainAxisAlignment: isMine ? MainAxisAlignment.end : MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        if (!isMine) _buildAvatar(message),
        SizedBox(width: 8),
        Flexible(
          child: Column(
            crossAxisAlignment: isMine ? CrossAxisAlignment.end : CrossAxisAlignment.start,
            children: [
              if (!isMine) Text(message.senderName, style: TextStyle(fontSize: 12, color: Colors.grey)),
              _buildBubble(message),
              _buildMessageStatus(message),
            ],
          ),
        ),
        SizedBox(width: 8),
        if (isMine) _buildAvatar(message),
      ],
    ),
  );
}

消息项根据是否为自己发送决定布局方向,自己的消息靠右显示,他人的消息靠左显示。Flexible确保气泡不会超出屏幕宽度。

消息气泡内容:

Widget _buildBubble(ChatMessage message) {
  final isMine = message.isMine;
  
  return Container(
    padding: EdgeInsets.all(12),
    decoration: BoxDecoration(
      color: isMine ? Colors.blue : Colors.grey.shade200,
      borderRadius: BorderRadius.only(
        topLeft: Radius.circular(16),
        topRight: Radius.circular(16),
        bottomLeft: Radius.circular(isMine ? 16 : 4),
        bottomRight: Radius.circular(isMine ? 4 : 16),
      ),
    ),
    child: _buildMessageContent(message),
  );
}

气泡使用不同颜色区分自己和他人的消息,圆角设计使气泡有指向发送者的效果。_buildMessageContent根据消息类型渲染不同内容。

OpenHarmony鸿蒙端实现

定义消息类型和数据接口:

type MessageType = 'text' | 'image' | 'file' | 'voice' | 'video' | 'location'
type MessageStatus = 'sending' | 'sent' | 'delivered' | 'read' | 'failed'

interface ChatMessage {
  id: string
  senderId: string
  senderName: string
  senderAvatar: string
  type: MessageType
  content: string
  time: number
  status: MessageStatus
  extra?: Record<string, any>
}

使用联合类型定义消息类型和状态,extra使用Record类型存储扩展数据。

聊天组件的基础结构:

@Component
struct ChatWidget {
  @Prop messages: ChatMessage[] = []
  @State inputText: string = ''
  @State showEmoji: boolean = false
  
  private scroller: Scroller = new Scroller()
  private onSendText: (text: string) => void = () => {}
  private onSendImage: (path: string) => void = () => {}
}

使用@State管理输入文本和表情面板状态,Scroller用于控制消息列表滚动。

消息列表:

@Builder
MessageList() {
  List({ scroller: this.scroller }) {
    ForEach(this.messages, (message: ChatMessage) => {
      ListItem() {
        this.MessageItem(message)
      }
    })
  }
  .width('100%')
  .layoutWeight(1)
  .listDirection(Axis.Vertical)
}

消息列表使用List组件渲染,layoutWeight(1)使列表占据剩余空间。ForEach遍历所有消息,每条消息使用MessageItem组件展示。

消息项:

@Builder
MessageItem(message: ChatMessage) {
  Row() {
    if (!this.isMine(message)) {
      Image(message.senderAvatar)
        .width(40)
        .height(40)
        .borderRadius(20)
    }
    
    Column() {
      if (!this.isMine(message)) {
        Text(message.senderName)
          .fontSize(12)
          .fontColor('#999999')
      }
      
      this.MessageBubble(message)
      this.MessageStatus(message)
    }
    .alignItems(this.isMine(message) ? HorizontalAlign.End : HorizontalAlign.Start)
    .layoutWeight(1)
    .margin({ left: 8, right: 8 })
    
    if (this.isMine(message)) {
      Image(message.senderAvatar)
        .width(40)
        .height(40)
        .borderRadius(20)
    }
  }
  .width('100%')
  .padding({ left: 16, right: 16, top: 8, bottom: 8 })
  .justifyContent(this.isMine(message) ? FlexAlign.End : FlexAlign.Start)
}

消息项根据发送者决定布局方向,自己的消息头像在右侧,他人的消息头像在左侧。alignItems控制气泡对齐方向。

消息气泡:

@Builder
MessageBubble(message: ChatMessage) {
  Column() {
    if (message.type === 'text') {
      Text(message.content)
        .fontSize(16)
        .fontColor(this.isMine(message) ? Color.White : '#333333')
    } else if (message.type === 'image') {
      Image(message.content)
        .width(200)
        .height(150)
        .borderRadius(8)
        .objectFit(ImageFit.Cover)
    }
  }
  .padding(12)
  .backgroundColor(this.isMine(message) ? '#1890FF' : '#F5F5F5')
  .borderRadius({
    topLeft: 16,
    topRight: 16,
    bottomLeft: this.isMine(message) ? 16 : 4,
    bottomRight: this.isMine(message) ? 4 : 16
  })
}

气泡根据消息类型渲染不同内容,文本消息显示文字,图片消息显示图片。borderRadius设置不对称圆角,形成指向发送者的效果。

输入区域:

@Builder
InputArea() {
  Row() {
    Image($r('app.media.emoji'))
      .width(28)
      .height(28)
      .onClick(() => {
        this.showEmoji = !this.showEmoji
      })
    
    TextInput({ placeholder: '输入消息', text: this.inputText })
      .layoutWeight(1)
      .height(40)
      .margin({ left: 8, right: 8 })
      .onChange((value: string) => {
        this.inputText = value
      })
    
    Image($r('app.media.attachment'))
      .width(28)
      .height(28)
      .onClick(() => this.pickImage())
    
    Button('发送')
      .type(ButtonType.Capsule)
      .height(36)
      .margin({ left: 8 })
      .enabled(this.inputText.trim().length > 0)
      .onClick(() => this.sendTextMessage())
  }
  .width('100%')
  .padding(12)
  .backgroundColor(Color.White)
}

输入区域包含表情按钮、文本输入框、附件按钮和发送按钮。发送按钮在输入内容为空时禁用,防止发送空消息。

发送消息:

private sendTextMessage() {
  if (this.inputText.trim().length === 0) return
  
  this.onSendText(this.inputText)
  this.inputText = ''
  
  setTimeout(() => {
    this.scroller.scrollToIndex(this.messages.length - 1)
  }, 100)
}

发送消息后清空输入框,并滚动到列表底部显示新消息。setTimeout确保消息添加到列表后再滚动。

消息状态显示:

@Builder
MessageStatus(message: ChatMessage) {
  if (this.isMine(message)) {
    Row() {
      Text(this.formatTime(message.time))
        .fontSize(10)
        .fontColor('#999999')
      
      if (message.status === 'sending') {
        LoadingProgress()
          .width(12)
          .height(12)
          .margin({ left: 4 })
      } else if (message.status === 'read') {
        Text('已读')
          .fontSize(10)
          .fontColor('#52C41A')
          .margin({ left: 4 })
      }
    }
    .margin({ top: 4 })
  }
}

消息状态显示发送时间和状态标识,发送中显示加载动画,已读显示绿色"已读"文字。只有自己发送的消息显示状态信息。

总结

本文详细介绍了Flutter和OpenHarmony平台上即时通讯聊天组件的开发方法。聊天组件是OA系统中促进团队协作的核心功能,需要支持多种消息类型和状态管理。两个平台都提供了列表组件和输入组件来实现聊天界面,开发者需要注意消息的布局方向和状态显示的处理。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐