|
|
|
@ -63,6 +63,8 @@ class _ImageItemState extends State<ImageItem> { |
|
|
|
Future.delayed(Duration(seconds: 1), () { |
|
|
|
if (mounted) { |
|
|
|
_checkRemotePathAndUpdateStatus(); |
|
|
|
_initImagePaths(); |
|
|
|
setState(() {}); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
@ -70,6 +72,23 @@ class _ImageItemState extends State<ImageItem> { |
|
|
|
Future.delayed(Duration(seconds: 2), () { |
|
|
|
if (mounted) { |
|
|
|
_checkRemotePathAndUpdateStatus(); |
|
|
|
_initImagePaths(); |
|
|
|
setState(() {}); |
|
|
|
} |
|
|
|
}); |
|
|
|
} else { |
|
|
|
// 接收方:定期检查路径更新,确保图片能正常显示 |
|
|
|
Future.delayed(Duration(milliseconds: 500), () { |
|
|
|
if (mounted) { |
|
|
|
_initImagePaths(); |
|
|
|
_checkAndDownloadImage(); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
Future.delayed(Duration(seconds: 1), () { |
|
|
|
if (mounted) { |
|
|
|
_initImagePaths(); |
|
|
|
_checkAndDownloadImage(); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
@ -131,6 +150,9 @@ class _ImageItemState extends State<ImageItem> { |
|
|
|
// 发送方不需要下载 |
|
|
|
if (widget.isSentByMe) return; |
|
|
|
|
|
|
|
// 先重新初始化路径(可能消息状态已更新) |
|
|
|
_initImagePaths(); |
|
|
|
|
|
|
|
// 如果已经有本地文件,检查是否有效 |
|
|
|
if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) { |
|
|
|
final file = File(_currentLocalPath!); |
|
|
|
@ -138,6 +160,11 @@ class _ImageItemState extends State<ImageItem> { |
|
|
|
final size = await file.length(); |
|
|
|
if (size > 1024) { // 文件至少1KB才认为有效 |
|
|
|
print('✅ 本地图片已存在且有效: $_currentLocalPath, 大小: ${size/1024}KB'); |
|
|
|
if (mounted) { |
|
|
|
setState(() { |
|
|
|
_hasError = false; |
|
|
|
}); |
|
|
|
} |
|
|
|
return; |
|
|
|
} else { |
|
|
|
print('⚠️ 本地图片文件太小或无效: $_currentLocalPath, 大小: $size bytes'); |
|
|
|
@ -153,10 +180,21 @@ class _ImageItemState extends State<ImageItem> { |
|
|
|
await _downloadImage(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
print('❌ 没有可用的远程图片路径'); |
|
|
|
setState(() { |
|
|
|
_hasError = true; |
|
|
|
}); |
|
|
|
print('❌ 没有可用的远程图片路径,尝试重新获取'); |
|
|
|
// 再次尝试初始化路径 |
|
|
|
_initImagePaths(); |
|
|
|
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) { |
|
|
|
if (!_isDownloading) { |
|
|
|
await _downloadImage(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
print('❌ 仍然没有可用的远程图片路径'); |
|
|
|
if (mounted) { |
|
|
|
setState(() { |
|
|
|
_hasError = true; |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -228,8 +266,15 @@ class _ImageItemState extends State<ImageItem> { |
|
|
|
} |
|
|
|
|
|
|
|
void _retryDownload() { |
|
|
|
if (!_isDownloading) { |
|
|
|
_downloadImage(); |
|
|
|
if (!_isDownloading && mounted) { |
|
|
|
// 重新初始化路径 |
|
|
|
_initImagePaths(); |
|
|
|
// 重置错误状态 |
|
|
|
setState(() { |
|
|
|
_hasError = false; |
|
|
|
}); |
|
|
|
// 重新检查和下载 |
|
|
|
_checkAndDownloadImage(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -317,9 +362,19 @@ class _ImageItemState extends State<ImageItem> { |
|
|
|
} |
|
|
|
|
|
|
|
// 接收方逻辑 |
|
|
|
// 如果有错误且正在下载 |
|
|
|
if (_hasError && !_isDownloading) { |
|
|
|
return _buildErrorState(width, height); |
|
|
|
// 优先显示本地图片(如果存在且有效) |
|
|
|
if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) { |
|
|
|
final file = File(_currentLocalPath!); |
|
|
|
if (file.existsSync()) { |
|
|
|
try { |
|
|
|
final size = file.lengthSync(); |
|
|
|
if (size > 1024) { // 文件至少1KB才认为有效 |
|
|
|
return _buildLocalImage(width, height); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
print('⚠️ 检查本地文件大小失败: $e'); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 如果正在下载 |
|
|
|
@ -327,12 +382,12 @@ class _ImageItemState extends State<ImageItem> { |
|
|
|
return _buildDownloadingState(width, height); |
|
|
|
} |
|
|
|
|
|
|
|
// 优先显示本地图片 |
|
|
|
if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) { |
|
|
|
return _buildLocalImage(width, height); |
|
|
|
// 如果有错误且不在下载中,显示错误状态(带重试按钮) |
|
|
|
if (_hasError && !_isDownloading) { |
|
|
|
return _buildErrorState(width, height); |
|
|
|
} |
|
|
|
|
|
|
|
// 显示远程图片 |
|
|
|
// 显示远程图片(如果本地图片不可用) |
|
|
|
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) { |
|
|
|
return _buildNetworkImage(width, height); |
|
|
|
} |
|
|
|
@ -384,33 +439,34 @@ class _ImageItemState extends State<ImageItem> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 3. 如果有远程路径,显示远程图片(但不要一直loading) |
|
|
|
// 3. 如果有远程路径,显示远程图片(使用 CachedNetworkImage 提供更好的缓存和错误处理) |
|
|
|
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) { |
|
|
|
print('🌐 发送方使用远程路径: $_currentRemotePath'); |
|
|
|
// 对于发送方,如果已经超时隐藏了loading状态,网络图片加载失败时也显示占位符而不是一直loading |
|
|
|
return Image.network( |
|
|
|
_currentRemotePath!, |
|
|
|
// 使用 CachedNetworkImage 替代 Image.network,提供更好的缓存和错误处理 |
|
|
|
return CachedNetworkImage( |
|
|
|
imageUrl: _currentRemotePath!, |
|
|
|
width: width, |
|
|
|
height: height, |
|
|
|
fit: BoxFit.cover, |
|
|
|
loadingBuilder: (context, child, loadingProgress) { |
|
|
|
if (loadingProgress == null) { |
|
|
|
print('✅ 发送方网络图片加载完成'); |
|
|
|
return child; |
|
|
|
} |
|
|
|
placeholder: (context, url) { |
|
|
|
// 如果已经超时隐藏了loading状态,不显示网络加载的loading |
|
|
|
if (_shouldHideProgressStatus) { |
|
|
|
print('⏭️ 发送方:已超时,跳过网络图片loading状态'); |
|
|
|
return _buildSenderPlaceholder(width, height); |
|
|
|
} |
|
|
|
// 只在真正加载时显示loading,但设置超时 |
|
|
|
// 只在真正加载时显示loading |
|
|
|
return _buildLoadingState(width, height); |
|
|
|
}, |
|
|
|
errorBuilder: (context, error, stackTrace) { |
|
|
|
print('❌ 发送方加载网络图片失败: $error'); |
|
|
|
errorWidget: (context, url, error) { |
|
|
|
print('❌ 发送方加载网络图片失败: $error, url: $url'); |
|
|
|
// 如果所有方法都失败,显示占位符而不是错误状态 |
|
|
|
return _buildSenderPlaceholder(width, height); |
|
|
|
}, |
|
|
|
// 添加重试配置 |
|
|
|
maxWidthDiskCache: 800, |
|
|
|
maxHeightDiskCache: 800, |
|
|
|
memCacheWidth: 800, |
|
|
|
memCacheHeight: 800, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
@ -450,34 +506,62 @@ class _ImageItemState extends State<ImageItem> { |
|
|
|
fit: BoxFit.cover, |
|
|
|
errorBuilder: (context, error, stackTrace) { |
|
|
|
print('❌ 加载本地图片失败: $error'); |
|
|
|
// 如果本地图片加载失败,尝试使用远程图片 |
|
|
|
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) { |
|
|
|
print('🔄 本地图片加载失败,尝试使用远程图片'); |
|
|
|
return _buildNetworkImage(width, height); |
|
|
|
} |
|
|
|
return _buildErrorState(width, height); |
|
|
|
}, |
|
|
|
); |
|
|
|
} else { |
|
|
|
print('❌ 本地图片文件不存在: $_currentLocalPath'); |
|
|
|
// 如果本地文件不存在,尝试使用远程图片 |
|
|
|
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) { |
|
|
|
print('🔄 本地文件不存在,尝试使用远程图片'); |
|
|
|
return _buildNetworkImage(width, height); |
|
|
|
} |
|
|
|
return _buildErrorState(width, height); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
print('❌ 加载本地图片异常: $e'); |
|
|
|
// 如果本地图片加载异常,尝试使用远程图片 |
|
|
|
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) { |
|
|
|
print('🔄 本地图片加载异常,尝试使用远程图片'); |
|
|
|
return _buildNetworkImage(width, height); |
|
|
|
} |
|
|
|
return _buildErrorState(width, height); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 构建网络图片 |
|
|
|
// 构建网络图片(接收方使用 CachedNetworkImage) |
|
|
|
Widget _buildNetworkImage(double width, double height) { |
|
|
|
return Image.network( |
|
|
|
_currentRemotePath!, |
|
|
|
// 使用 CachedNetworkImage 替代 Image.network,提供更好的缓存和错误处理 |
|
|
|
return CachedNetworkImage( |
|
|
|
imageUrl: _currentRemotePath!, |
|
|
|
width: width, |
|
|
|
height: height, |
|
|
|
fit: BoxFit.cover, |
|
|
|
loadingBuilder: (context, child, loadingProgress) { |
|
|
|
if (loadingProgress == null) return child; |
|
|
|
placeholder: (context, url) { |
|
|
|
return _buildLoadingState(width, height); |
|
|
|
}, |
|
|
|
errorBuilder: (context, error, stackTrace) { |
|
|
|
print('❌ 加载网络图片失败: $error'); |
|
|
|
errorWidget: (context, url, error) { |
|
|
|
print('❌ 接收方加载网络图片失败: $error, url: $url'); |
|
|
|
// 尝试重新下载 |
|
|
|
if (!_isDownloading) { |
|
|
|
Future.microtask(() { |
|
|
|
if (mounted && !_isDownloading) { |
|
|
|
_checkAndDownloadImage(); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
return _buildErrorState(width, height); |
|
|
|
}, |
|
|
|
// 添加重试配置 |
|
|
|
maxWidthDiskCache: 800, |
|
|
|
maxHeightDiskCache: 800, |
|
|
|
memCacheWidth: 800, |
|
|
|
memCacheHeight: 800, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
|