Browse Source
feat(message): 实现图片消息展示功能- 新增 ImageItem 组件用于展示图片消息
feat(message): 实现图片消息展示功能- 新增 ImageItem 组件用于展示图片消息
- 支持网络图片加载与错误处理 - 添加图片尺寸自适应逻辑- 优化消息气泡样式与布局 - 移除模拟推荐用户列表代码- 删除 IM 登录后的测试消息发送逻辑ios
4 changed files with 214 additions and 135 deletions
Unified View
Diff Options
-
1lib/im/im_manager.dart
-
73lib/pages/message/conversation_tab.dart
-
195lib/widget/message/image_item.dart
-
80lib/widget/message/message_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], |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save