diff --git a/lib/im/im_manager.dart b/lib/im/im_manager.dart index d5715dc..c7b936f 100644 --- a/lib/im/im_manager.dart +++ b/lib/im/im_manager.dart @@ -122,7 +122,6 @@ class IMManager { var userId = storage.read('userId'); await EMClient.getInstance.logout(); await EMClient.getInstance.loginWithToken(userId, token); - await sendTextMessage('哈哈哈哈', '1114267797208305664'); // 注册监听器 _registerListeners(); print('IM login successful'); diff --git a/lib/pages/message/conversation_tab.dart b/lib/pages/message/conversation_tab.dart index 01cbd75..f15fd1d 100644 --- a/lib/pages/message/conversation_tab.dart +++ b/lib/pages/message/conversation_tab.dart @@ -16,24 +16,12 @@ class ConversationTab extends StatefulWidget { class _ConversationTabState extends State with AutomaticKeepAliveClientMixin { final ConversationController controller = Get.find(); - - // 模拟数据 - 顶部推荐用户列表 - final List> _recommendedUsers = [ - {"id": 1, "avatar": Assets.imagesAvatarsExample, "type": "相亲"}, - {"id": 2, "avatar": Assets.imagesAvatarsExample, "type": "交友"}, - {"id": 3, "avatar": Assets.imagesAvatarsExample, "type": "相亲"}, - {"id": 4, "avatar": Assets.imagesAvatarsExample, "type": "相亲"}, - {"id": 5, "avatar": Assets.imagesAvatarsExample, "type": "相亲"}, - {"id": 6, "avatar": Assets.imagesAvatarsExample, "type": "相亲"}, - ]; @override Widget build(BuildContext context) { super.build(context); return Column( children: [ - // 推荐用户横向滚动列表 - _buildRecommendedUsers(), // 聊天列表 Expanded( child: Obx(() { @@ -74,67 +62,6 @@ class _ConversationTabState extends State with AutomaticKeepAli ); } - // 构建推荐用户横向滚动列表 - Widget _buildRecommendedUsers() { - return SizedBox( - height: 60, - child: ListView.builder( - scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric(horizontal: 16), - itemCount: _recommendedUsers.length, - itemBuilder: (context, index) { - final user = _recommendedUsers[index]; - final bool isSelected = index == 1; // 模拟选中状态 - - return Container( - margin: const EdgeInsets.only(right: 12), - child: Column( - children: [ - Stack( - children: [ - Container( - width: 60, - height: 60, - decoration: BoxDecoration( - border: Border.all( - color: isSelected ? Colors.blue : Colors.transparent, - width: 2, - ), - borderRadius: BorderRadius.circular(30), - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(30), - child: Image.asset(user["avatar"], fit: BoxFit.cover), - ), - ), - if (user["type"] == "交友") - Positioned( - bottom: 0, - right: 0, - child: Container( - width: 20, - height: 20, - decoration: BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.circular(10), - ), - child: const Icon( - Icons.people, - size: 12, - color: Colors.white, - ), - ), - ), - ], - ), - ], - ), - ); - }, - ), - ); - } - // 构建会话项 Widget _buildConversationItem(EMConversation conversation) { // 使用FutureBuilder获取未读消息数和最新消息 diff --git a/lib/widget/message/image_item.dart b/lib/widget/message/image_item.dart new file mode 100644 index 0000000..f688521 --- /dev/null +++ b/lib/widget/message/image_item.dart @@ -0,0 +1,195 @@ +import 'dart:io'; +import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:im_flutter_sdk/im_flutter_sdk.dart'; + +class ImageItem extends StatelessWidget { + final EMImageMessageBody imageBody; + final bool isSentByMe; + final bool showTime; + final String formattedTime; + + const ImageItem({ + required this.imageBody, + required this.isSentByMe, + required this.showTime, + required this.formattedTime, + super.key, + }); + + @override + Widget build(BuildContext context) { + 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.start, + children: [ + if (!isSentByMe) _buildAvatar(), + if (!isSentByMe) SizedBox(width: 8.w), + Container( + margin: EdgeInsets.only(top: 10.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: _buildImage(), + ), + 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 _buildImage() { + // 计算图片尺寸,限制最大宽度为200 + double maxWidth = 200.w; + double maxHeight = 200.w; + double width = maxWidth; + double height = maxHeight; + Get.log(imageBody.thumbnailLocalPath ?? ''); + // 如果有图片尺寸信息,根据比例调整 + if (imageBody.width != null && imageBody.width! > 0 && + imageBody.height != null && imageBody.height! > 0) { + final aspectRatio = imageBody.width! / imageBody.height!; + if (aspectRatio > 1) { + // 宽图 + width = maxWidth; + height = maxWidth / aspectRatio; + } else { + // 高图 + height = maxHeight; + width = maxHeight * aspectRatio; + } + } + + // 尝试显示网络缩略图 + if (imageBody.thumbnailRemotePath != null && imageBody.thumbnailRemotePath!.isNotEmpty) { + return Image.network( + imageBody.thumbnailRemotePath!, + width: width, + height: height, + fit: BoxFit.cover, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return _buildLoadingContainer(width, height); + }, + errorBuilder: (context, error, stackTrace) { + return _buildErrorContainer(width, height); + }, + ); + } + + // 尝试显示网络原图 + if (imageBody.remotePath != null && imageBody.remotePath!.isNotEmpty) { + return Image.network( + imageBody.remotePath!, + width: width, + height: height, + fit: BoxFit.cover, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return _buildLoadingContainer(width, height); + }, + errorBuilder: (context, error, stackTrace) { + return _buildErrorContainer(width, height); + }, + ); + } + + // 默认显示错误容器 + return _buildErrorContainer(width, height); + } + + // 构建加载中的容器 + Widget _buildLoadingContainer(double width, double height) { + return Container( + width: width, + height: height, + padding: EdgeInsets.all(8.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.w), + color: Colors.grey[200], + ), + alignment: Alignment.center, + child: CircularProgressIndicator( + strokeWidth: 2.w, + color: Colors.grey[400], + ), + ); + } + + // 构建错误容器 + Widget _buildErrorContainer(double width, double height) { + return Container( + width: width, + height: height, + padding: EdgeInsets.all(8.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.w), + color: Colors.grey[200], + ), + alignment: Alignment.center, + child: Icon( + Icons.image_not_supported, + size: 32.w, + color: Colors.grey[400], + ), + ); + } +} \ No newline at end of file diff --git a/lib/widget/message/message_item.dart b/lib/widget/message/message_item.dart index 30db38a..a879f2d 100644 --- a/lib/widget/message/message_item.dart +++ b/lib/widget/message/message_item.dart @@ -2,7 +2,8 @@ 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'; +import 'text_item.dart'; +import 'image_item.dart'; class MessageItem extends StatelessWidget { final EMMessage message; @@ -18,54 +19,24 @@ class MessageItem extends StatelessWidget { @override Widget build(BuildContext context) { - // 只处理文本消息,其他类型消息可以根据需要扩展 + // 处理文本消息 if (message.body.type == MessageType.TXT) { final textBody = message.body as EMTextMessageBody; - return Column( - children: [ - // 显示时间(如果距离上一条消息超过20分钟) - if (shouldShowTime()) _buildTimeLabel(), - Container( - padding: EdgeInsets.symmetric( - horizontal: 16.w, - vertical: 8.h, - ), - child: Row( - mainAxisAlignment: isSentByMe ? MainAxisAlignment.end : MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (!isSentByMe) _buildAvatar(), - if (!isSentByMe) SizedBox(width: 8.w), - Container( - constraints: BoxConstraints(maxWidth: 240.w), - margin: EdgeInsets.only(top: 10.h), - padding: EdgeInsets.symmetric( - horizontal: 12.w, - vertical: 8.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: Text( - textBody.content, - style: TextStyle( - fontSize: 14.sp, - color: isSentByMe ? Colors.white : Colors.black, // 发送方白色文字,接收方黑色文字 - ), - ), - ), - if (isSentByMe) SizedBox(width: 8.w), - if (isSentByMe) _buildAvatar(), - ], - ), - ), - ], + return TextItem( + textBody: textBody, + isSentByMe: isSentByMe, + showTime: shouldShowTime(), + formattedTime: formatMessageTime(message.serverTime), + ); + } + // 处理图片消息 + else if (message.body.type == MessageType.IMAGE) { + final imageBody = message.body as EMImageMessageBody; + return ImageItem( + imageBody: imageBody, + isSentByMe: isSentByMe, + showTime: shouldShowTime(), + formattedTime: formatMessageTime(message.serverTime), ); } @@ -138,18 +109,5 @@ class MessageItem extends StatelessWidget { } } - // 构建头像 - 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, - ), - ), - ); - } + } \ No newline at end of file