import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; import '../../generated/assets.dart'; /// 通话类型 enum CallType { voice, // 语音通话 video, // 视频通话 } /// 通话状态 enum CallStatus { calling, // 通话中(显示时长) missed, // 未接听 cancelled, // 已取消 rejected, // 已拒绝 } class CallItem extends StatelessWidget { final EMMessage message; final bool isSentByMe; final bool showTime; final String formattedTime; final VoidCallback? onResend; const CallItem({ required this.message, required this.isSentByMe, required this.showTime, required this.formattedTime, this.onResend, super.key, }); /// 从消息内容中解析通话信息(使用特殊的JSON格式) Map? _parseCallInfo() { try { if (message.body.type == MessageType.TXT) { final textBody = message.body as EMTextMessageBody; final content = textBody.content; // 检查是否是通话消息(以 [CALL:] 开头) if (content != null && content.startsWith('[CALL:]')) { final jsonStr = content.substring(7); // 移除 '[CALL:]' 前缀 return jsonDecode(jsonStr) as Map; } } } catch (e) { print('解析通话信息失败: $e'); } return null; } /// 从消息中解析通话类型 CallType? _getCallType() { final callInfo = _parseCallInfo(); if (callInfo != null) { final callTypeStr = callInfo['callType'] as String?; if (callTypeStr == 'voice') { return CallType.voice; } else if (callTypeStr == 'video') { return CallType.video; } } return null; } /// 从消息中解析通话状态 CallStatus? _getCallStatus() { final callInfo = _parseCallInfo(); if (callInfo != null) { final statusStr = callInfo['callStatus'] as String?; if (statusStr == 'calling') { return CallStatus.calling; } else if (statusStr == 'missed') { return CallStatus.missed; } else if (statusStr == 'cancelled') { return CallStatus.cancelled; } else if (statusStr == 'rejected') { return CallStatus.rejected; } } return CallStatus.missed; // 默认未接听 } /// 获取通话时长(秒) int? _getCallDuration() { final callInfo = _parseCallInfo(); if (callInfo != null) { return callInfo['callDuration'] as int?; } return null; } /// 格式化通话时长 String _formatDuration(int seconds) { final minutes = seconds ~/ 60; final secs = seconds % 60; if (minutes > 0) { return '${minutes}:${secs.toString().padLeft(2, '0')}'; } else { return '${secs}秒'; } } /// 获取图标资源 String _getIconAsset() { final callType = _getCallType(); final callStatus = _getCallStatus(); if (isSentByMe) { // 自己发起的通话 if (callType == CallType.video) { return Assets.imagesSendVideoCall; } else { return Assets.imagesSendCall; } } else { // 别人发起的通话 if (callStatus == CallStatus.rejected) { return Assets.imagesRejectCall; } else { return Assets.imagesAcceptCall; } } } /// 获取状态文本 String _getStatusText() { final callType = _getCallType(); final callStatus = _getCallStatus(); final duration = _getCallDuration(); if (callStatus == CallStatus.calling && duration != null) { // 通话中,显示时长 return _formatDuration(duration); } else if (callStatus == CallStatus.missed) { return callType == CallType.video ? '未接听视频通话' : '未接听语音通话'; } else if (callStatus == CallStatus.cancelled) { return callType == CallType.video ? '已取消视频通话' : '已取消语音通话'; } else if (callStatus == CallStatus.rejected) { return callType == CallType.video ? '已拒绝视频通话' : '已拒绝语音通话'; } else { return callType == CallType.video ? '视频通话' : '语音通话'; } } @override Widget build(BuildContext context) { final callType = _getCallType(); if (callType == null) { // 如果解析失败,显示默认文本消息 return SizedBox.shrink(); } final iconAsset = _getIconAsset(); final statusText = _getStatusText(); final callStatus = _getCallStatus(); final isCalling = callStatus == CallStatus.calling; return Column( children: [ // 显示时间 if (showTime) _buildTimeLabel(), Container( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), child: Row( mainAxisAlignment: isSentByMe ? MainAxisAlignment.end : MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ if (!isSentByMe) _buildAvatar(), if (!isSentByMe) SizedBox(width: 8.w), // 发送消息时,状态在左侧 if (isSentByMe) Align( alignment: Alignment.center, child: Container( margin: EdgeInsets.only(top: 10.h), child: _buildMessageStatus(), ), ), if (isSentByMe) SizedBox(width: 10.w), // 通话消息容器 Container( constraints: BoxConstraints(maxWidth: 200.w), margin: EdgeInsets.only(top: 10.h), padding: EdgeInsets.symmetric( horizontal: 16.w, vertical: 12.h, ), decoration: BoxDecoration( color: isSentByMe ? Color(0xff8E7BF6) : Colors.white, borderRadius: BorderRadius.only( topLeft: isSentByMe ? Radius.circular(12.w) : Radius.circular(0), topRight: isSentByMe ? Radius.circular(0) : Radius.circular(12.w), bottomLeft: Radius.circular(12.w), bottomRight: Radius.circular(12.w), ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ // 图标 Image.asset( iconAsset, width: 24.w, height: 24.w, fit: BoxFit.contain, ), SizedBox(width: 8.w), // 状态文本 Flexible( child: Text( statusText, style: TextStyle( fontSize: 14.sp, color: isSentByMe ? Colors.white : Colors.black87, fontWeight: isCalling ? FontWeight.w500 : FontWeight.normal, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), ], ), ), if (isSentByMe) SizedBox(width: 8.w), if (isSentByMe) _buildAvatar(), ], ), ), ], ); } // 构建时间标签 Widget _buildTimeLabel() { return Container( alignment: Alignment.center, padding: EdgeInsets.symmetric(horizontal: 16.w), child: Container( padding: EdgeInsets.symmetric(horizontal: 12.w), child: Text( formattedTime, style: TextStyle(fontSize: 12.sp, color: Colors.grey), ), ), ); } // 构建头像 Widget _buildAvatar() { return Container( width: 40.w, height: 40.w, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20.w), image: DecorationImage( image: AssetImage(Assets.imagesAvatarsExample), fit: BoxFit.cover, ), ), ); } // 构建消息状态(发送中、已发送、失败重发) Widget _buildMessageStatus() { if (!isSentByMe) { return SizedBox.shrink(); } final status = message.status; if (status == MessageStatus.FAIL) { // 发送失败,显示重发按钮 return GestureDetector( onTap: onResend, child: Container( width: 20.w, height: 20.w, decoration: BoxDecoration( color: Colors.red.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon( Icons.refresh, size: 14.w, color: Colors.red, ), ), ); } else if (status == MessageStatus.PROGRESS) { // 发送中,显示加载动画 return Container( width: 16.w, height: 16.w, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.grey), ), ); } else { // 发送成功,不显示任何状态 return SizedBox.shrink(); } } }