import 'dart:io'; 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'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:dating_touchme_app/generated/assets.dart'; import 'package:dating_touchme_app/pages/message/image_viewer_page.dart'; class ImageItem extends StatefulWidget { final EMImageMessageBody imageBody; final bool isSentByMe; final bool showTime; final String formattedTime; final EMMessage message; final VoidCallback? onResend; const ImageItem({ required this.imageBody, required this.isSentByMe, required this.showTime, required this.formattedTime, required this.message, this.onResend, super.key, }); @override State createState() => _ImageItemState(); } class _ImageItemState extends State { bool _isDownloading = false; bool _hasError = false; String? _currentLocalPath; String? _currentRemotePath; bool _shouldHideProgressStatus = false; // 用于判断是否应该隐藏PROGRESS状态 @override void initState() { super.initState(); _initImagePaths(); _checkAndDownloadImage(); // 对于发送方,添加智能检测:检查远程路径是否可用 if (widget.isSentByMe) { // 立即检查是否有远程路径(说明已经上传成功) _checkRemotePathAndUpdateStatus(); // 延迟检查,等待消息状态更新 Future.delayed(Duration(milliseconds: 500), () { if (mounted) { _initImagePaths(); _checkRemotePathAndUpdateStatus(); setState(() {}); } }); // 如果1秒后状态还是PROGRESS,但有远程路径,认为发送成功,隐藏loading状态 Future.delayed(Duration(seconds: 1), () { if (mounted) { _checkRemotePathAndUpdateStatus(); } }); // 如果2秒后状态还是PROGRESS,但有远程路径,认为发送成功,隐藏loading状态 Future.delayed(Duration(seconds: 2), () { if (mounted) { _checkRemotePathAndUpdateStatus(); } }); } } // 检查远程路径并更新状态 void _checkRemotePathAndUpdateStatus() { // 重新获取路径(可能SDK已经更新) final body = widget.message.body; if (body is EMImageMessageBody) { final remotePath = body.remotePath; // 如果有远程路径,说明图片已经上传成功,即使状态还是PROGRESS也隐藏loading if (remotePath != null && remotePath.isNotEmpty) { if (!_shouldHideProgressStatus) { print('✅ 发送方:检测到远程路径,隐藏loading状态: $remotePath'); setState(() { _shouldHideProgressStatus = true; _currentRemotePath = remotePath; // 更新远程路径 }); } } } } void _initImagePaths() { // 对于发送方,优先使用构造函数传入的imageBody(发送时的原始路径) // 对于接收方,从消息对象中获取 if (widget.isSentByMe) { _currentLocalPath = widget.imageBody.localPath; _currentRemotePath = widget.imageBody.remotePath; // 如果构造函数传入的路径为空,再尝试从消息对象获取 if ((_currentLocalPath == null || _currentLocalPath!.isEmpty) && widget.message.body is EMImageMessageBody) { final body = widget.message.body as EMImageMessageBody; _currentLocalPath = body.localPath; _currentRemotePath = body.remotePath; } } else { // 接收方:优先使用消息体中的路径 final body = widget.message.body; if (body is EMImageMessageBody) { _currentLocalPath = body.localPath; _currentRemotePath = body.remotePath; } else { // 备用:使用构造函数传入的imageBody _currentLocalPath = widget.imageBody.localPath; _currentRemotePath = widget.imageBody.remotePath; } } print('📸 图片路径初始化:'); print(' 本地路径: $_currentLocalPath'); print(' 远程路径: $_currentRemotePath'); print(' 是否发送方: ${widget.isSentByMe}'); } Future _checkAndDownloadImage() async { // 发送方不需要下载 if (widget.isSentByMe) return; // 如果已经有本地文件,检查是否有效 if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) { final file = File(_currentLocalPath!); if (await file.exists()) { final size = await file.length(); if (size > 1024) { // 文件至少1KB才认为有效 print('✅ 本地图片已存在且有效: $_currentLocalPath, 大小: ${size/1024}KB'); return; } else { print('⚠️ 本地图片文件太小或无效: $_currentLocalPath, 大小: $size bytes'); } } else { print('⚠️ 本地图片文件不存在: $_currentLocalPath'); } } // 需要下载图片 if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) { if (!_isDownloading) { await _downloadImage(); } } else { print('❌ 没有可用的远程图片路径'); setState(() { _hasError = true; }); } } Future _downloadImage() async { if (_isDownloading) return; setState(() { _isDownloading = true; _hasError = false; }); try { print('📥 开始下载图片消息: ${widget.message.msgId}'); // 使用环信SDK下载图片(downloadAttachment 返回 void) await EMClient.getInstance.chatManager .downloadAttachment(widget.message); // 等待一小段时间,确保文件写入完成 await Future.delayed(Duration(milliseconds: 300)); // 下载后,从消息对象获取新的本地路径(下载后会自动更新 body 中的路径) final body = widget.message.body; if (body is EMImageMessageBody) { setState(() { _currentLocalPath = body.localPath; _currentRemotePath = body.remotePath; }); print('✅ 图片下载完成: $_currentLocalPath'); // 验证文件 if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) { final file = File(_currentLocalPath!); if (await file.exists()) { final size = await file.length(); print('✅ 文件验证成功,大小: ${size/1024}KB'); } else { print('⚠️ 下载后文件不存在: $_currentLocalPath'); setState(() { _hasError = true; }); } } else { print('⚠️ 下载后本地路径为空'); setState(() { _hasError = true; }); } } else { print('❌ 消息体类型错误: ${body.runtimeType}'); setState(() { _hasError = true; }); } } catch (e) { print('❌ 下载图片失败: $e'); SmartDialog.showToast('图片下载失败'); setState(() { _hasError = true; }); } finally { if (mounted) { setState(() { _isDownloading = false; }); } } } void _retryDownload() { if (!_isDownloading) { _downloadImage(); } } @override Widget build(BuildContext context) { return Column( children: [ if (widget.showTime) _buildTimeLabel(), Container( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), child: Builder( builder: (context) { // 计算图片尺寸 double maxWidth = 180.w; double width = maxWidth; double height = width * (304 / 289); final imageHeight = height + 10.h; return Row( mainAxisAlignment: widget.isSentByMe ? MainAxisAlignment.end : MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (!widget.isSentByMe) _buildAvatar(), if (!widget.isSentByMe) SizedBox(width: 8.w), // 发送消息的状态 if (widget.isSentByMe) SizedBox( height: imageHeight, child: Center( child: _buildMessageStatus(), ), ), if (widget.isSentByMe) SizedBox(width: 10.w), // 图片容器 GestureDetector( onTap: _onImageTap, child: Container( margin: EdgeInsets.only(top: 10.h), decoration: BoxDecoration( color: widget.isSentByMe ? Color(0xff8E7BF6) : Colors.white, borderRadius: BorderRadius.circular(18.w), ), child: ClipRRect( borderRadius: BorderRadius.circular(18.w), child: _buildImageContent(width, height), ), ), ), if (widget.isSentByMe) SizedBox(width: 8.w), if (widget.isSentByMe) _buildAvatar(), ], ); }, ), ), ], ); } // 构建图片内容 Widget _buildImageContent(double width, double height) { // 发送方和接收方的逻辑不同 if (widget.isSentByMe) { // 发送方:优先显示本地图片(发送时的原始路径) return _buildSenderImage(width, height); } // 接收方逻辑 // 如果有错误且正在下载 if (_hasError && !_isDownloading) { return _buildErrorState(width, height); } // 如果正在下载 if (_isDownloading) { return _buildDownloadingState(width, height); } // 优先显示本地图片 if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) { return _buildLocalImage(width, height); } // 显示远程图片 if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) { return _buildNetworkImage(width, height); } // 默认显示错误状态 return _buildErrorState(width, height); } // 发送方构建图片 Widget _buildSenderImage(double width, double height) { // 1. 优先尝试构造函数传入的本地路径(发送时的原始路径) if (widget.imageBody.localPath.isNotEmpty) { final file = File(widget.imageBody.localPath); if (file.existsSync()) { print('✅ 发送方使用原始本地路径: ${widget.imageBody.localPath}'); return Image.file( file, width: width, height: height, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { print('❌ 发送方加载本地图片失败: $error'); // 如果构造函数路径失败,返回占位符 return _buildSenderPlaceholder(width, height); }, ); } else { print('⚠️ 发送方原始本地路径文件不存在: ${widget.imageBody.localPath}'); } } // 2. 尝试当前本地路径(可能是从消息对象获取的) if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) { final file = File(_currentLocalPath!); if (file.existsSync()) { print('✅ 发送方使用当前本地路径: $_currentLocalPath'); return Image.file( file, width: width, height: height, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { print('❌ 发送方加载当前本地图片失败: $error'); return _buildSenderPlaceholder(width, height); }, ); } else { print('⚠️ 发送方当前本地路径文件不存在: $_currentLocalPath'); } } // 3. 如果有远程路径,显示远程图片(但不要一直loading) if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) { print('🌐 发送方使用远程路径: $_currentRemotePath'); // 对于发送方,如果已经超时隐藏了loading状态,网络图片加载失败时也显示占位符而不是一直loading return Image.network( _currentRemotePath!, width: width, height: height, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) { print('✅ 发送方网络图片加载完成'); return child; } // 如果已经超时隐藏了loading状态,不显示网络加载的loading if (_shouldHideProgressStatus) { print('⏭️ 发送方:已超时,跳过网络图片loading状态'); return _buildSenderPlaceholder(width, height); } // 只在真正加载时显示loading,但设置超时 return _buildLoadingState(width, height); }, errorBuilder: (context, error, stackTrace) { print('❌ 发送方加载网络图片失败: $error'); // 如果所有方法都失败,显示占位符而不是错误状态 return _buildSenderPlaceholder(width, height); }, ); } // 4. 所有方法都失败,显示占位符 print('⚠️ 发送方:所有路径都不可用,显示占位符'); return _buildSenderPlaceholder(width, height); } // 发送方占位符(避免显示错误状态,因为消息已发送成功) Widget _buildSenderPlaceholder(double width, double height) { return Container( width: width, height: height, decoration: BoxDecoration( borderRadius: BorderRadius.circular(18.w), color: Color(0xff8E7BF6).withOpacity(0.1), ), child: Center( child: Icon( Icons.image, size: 32.w, color: Color(0xff8E7BF6).withOpacity(0.5), ), ), ); } // 构建本地图片 Widget _buildLocalImage(double width, double height) { try { final file = File(_currentLocalPath!); if (file.existsSync()) { return Image.file( file, width: width, height: height, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { print('❌ 加载本地图片失败: $error'); return _buildErrorState(width, height); }, ); } else { print('❌ 本地图片文件不存在: $_currentLocalPath'); return _buildErrorState(width, height); } } catch (e) { print('❌ 加载本地图片异常: $e'); return _buildErrorState(width, height); } } // 构建网络图片 Widget _buildNetworkImage(double width, double height) { return Image.network( _currentRemotePath!, width: width, height: height, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return _buildLoadingState(width, height); }, errorBuilder: (context, error, stackTrace) { print('❌ 加载网络图片失败: $error'); return _buildErrorState(width, height); }, ); } // 构建加载状态 Widget _buildLoadingState(double width, double height) { return Container( width: width, height: height, decoration: BoxDecoration( borderRadius: BorderRadius.circular(18.w), color: Colors.grey[200], ), child: Center( child: CircularProgressIndicator( strokeWidth: 2.w, valueColor: AlwaysStoppedAnimation(Colors.grey[400]!), ), ), ); } // 构建下载中状态 Widget _buildDownloadingState(double width, double height) { return Container( width: width, height: height, decoration: BoxDecoration( borderRadius: BorderRadius.circular(18.w), color: Colors.grey[200], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator( strokeWidth: 2.w, valueColor: AlwaysStoppedAnimation(Color(0xff8E7BF6)), ), SizedBox(height: 8.h), Text( '下载中...', style: TextStyle( fontSize: 12.sp, color: Colors.grey[600], ), ), ], ), ); } // 构建错误状态 Widget _buildErrorState(double width, double height) { return Container( width: width, height: height, decoration: BoxDecoration( borderRadius: BorderRadius.circular(18.w), color: Colors.grey[200], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.broken_image, size: 32.w, color: Colors.grey[400], ), SizedBox(height: 8.h), Text( '图片加载失败', style: TextStyle( fontSize: 12.sp, color: Colors.grey[600], ), ), if (!widget.isSentByMe && _hasError) Padding( padding: EdgeInsets.only(top: 8.h), child: ElevatedButton( onPressed: _retryDownload, style: ElevatedButton.styleFrom( padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 4.h), backgroundColor: Color(0xff8E7BF6), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.w), ), ), child: Text( '重新加载', style: TextStyle( fontSize: 10.sp, color: Colors.white, ), ), ), ), ], ), ); } // 构建时间标签 Widget _buildTimeLabel() { return Container( alignment: Alignment.center, padding: EdgeInsets.symmetric(horizontal: 16.w), child: Container( padding: EdgeInsets.symmetric(horizontal: 12.w), child: Text( widget.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 (!widget.isSentByMe) return SizedBox.shrink(); final status = widget.message.status; if (status == MessageStatus.FAIL) { return GestureDetector( onTap: widget.onResend, child: Container( width: 44.w, height: 26.h, decoration: BoxDecoration( color: Color.fromRGBO(248, 85, 66, 1), borderRadius: BorderRadius.circular(8.w), ), child: Center( child: Text( '重发', style: TextStyle( fontSize: 12.sp, color: Colors.white, ), ), ), ), ); } else if (status == MessageStatus.PROGRESS) { // 如果应该隐藏PROGRESS状态(已超时且有远程路径),不显示loading if (_shouldHideProgressStatus) { return SizedBox.shrink(); } return Container( width: 16.w, height: 16.w, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.grey), ), ); } else { return SizedBox.shrink(); } } // 点击图片事件 void _onImageTap() { // 检查是否有可显示的图片 bool hasImage = false; String? imagePath; String? imageUrl; // 优先使用本地图片 if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) { final file = File(_currentLocalPath!); if (file.existsSync()) { imagePath = _currentLocalPath; hasImage = true; } } // 如果没有本地图片,使用远程图片 if (!hasImage && _currentRemotePath != null && _currentRemotePath!.isNotEmpty) { imageUrl = _currentRemotePath; hasImage = true; } if (hasImage) { Get.to( () => ImageViewerPage( imagePath: imagePath, imageUrl: imageUrl, ), transition: Transition.fade, duration: const Duration(milliseconds: 300), ); } else { SmartDialog.showToast('图片暂时无法查看'); } } }