
Image Widget加载状态管理
概述
Image组件在加载图片时会经历多个状态,包括加载中、加载成功和加载失败。合理管理这些状态是提升用户体验的关键。在实际应用中,图片加载往往不是一帆风顺的,网络波动、资源限制、服务器响应延迟等因素都会影响图片的加载过程。如果对加载状态管理不善,不仅会导致用户等待时间过长,还可能引发页面布局的抖动,甚至出现白屏等严重问题。因此,深入理解图片加载的各个状态,掌握状态管理的技术要点,对于构建高质量的Flutter应用至关重要。
加载状态分类
图片加载过程中主要有三种状态:
| 状态 | 说明 | 用户感知 | 技术特征 | UI表现 |
|---|---|---|---|---|
| loading | 正在加载图片 | 显示加载指示器 | 网络请求进行中 | CircularProgress 或自定义加载动画 |
| success | 图片加载成功 | 显示完整图片 | 数据已获取并解码 | 显示完整的Image组件 |
| error | 图片加载失败 | 显示错误提示 | 请求失败或数据异常 | 错误图标、文字说明和重试按钮 |
此外,还可以细分出一些过渡状态:
| 子状态 | 父状态 | 触发条件 | 处理建议 |
|---|---|---|---|
| connecting | loading | 建立网络连接 | 显示连接中提示 |
| downloading | loading | 数据传输中 | 显示下载进度 |
| decoding | loading | 图片解码中 | 显示解码进度 |
| cached | success | 从缓存读取 | 可快速显示,无需动画 |
| timeout | error | 请求超时 | 提供重试选项 |
| network_error | error | 网络错误 | 检查网络,提供重试 |
命中
未命中
否
是
否
是
否
是
重试
忽略
开始加载
检查缓存
从缓存读取
建立连接
显示图片
连接成功?
网络错误
下载数据
下载成功?
下载失败
解码图片
解码成功?
格式错误
显示错误提示
用户操作
结束
状态管理实现
1. 定义状态枚举
使用枚举类型明确定义三种状态,可以让代码更加清晰和易于维护。通过枚举,我们可以确保状态的类型安全,避免使用魔法字符串或数字来表示状态,同时也可以方便地在switch语句中进行模式匹配。
dart
/// 图片加载状态枚举
enum ImageLoadState {
/// 加载中状态
loading,
/// 加载成功状态
success,
/// 加载失败状态
error,
/// 加载取消状态(扩展)
cancelled,
}
/// 图片加载状态扩展方法
extension ImageLoadStateExtension on ImageLoadState {
String get displayName {
switch (this) {
case ImageLoadState.loading:
return '加载中';
case ImageLoadState.success:
return '加载成功';
case ImageLoadState.error:
return '加载失败';
case ImageLoadState.cancelled:
return '已取消';
}
}
bool get isLoading => this == ImageLoadState.loading;
bool get isSuccess => this == ImageLoadState.success;
bool get isError => this == ImageLoadState.error;
bool get isCancelled => this == ImageLoadState.cancelled;
bool get isFinished => this == ImageLoadState.success ||
this == ImageLoadState.error ||
this == ImageLoadState.cancelled;
}
2. 创建状态管理组件
dart
/// 图片加载器组件
/// 支持状态管理、错误处理和重试功能
class ImageLoader extends StatefulWidget {
final String imageUrl;
final Widget? loadingWidget;
final Widget? errorWidget;
final VoidCallback? onRetry;
final VoidCallback? onSuccess;
final VoidCallback? onError;
final Duration? timeout;
const ImageLoader({
super.key,
required this.imageUrl,
this.loadingWidget,
this.errorWidget,
this.onRetry,
this.onSuccess,
this.onError,
this.timeout,
});
@override
State<ImageLoader> createState() => _ImageLoaderState();
}
class _ImageLoaderState extends State<ImageLoader> {
ImageLoadState _loadState = ImageLoadState.loading;
String? _errorMessage;
@override
void initState() {
super.initState();
_loadImage();
}
/// 加载图片的核心逻辑
Future<void> _loadImage() async {
if (mounted) {
setState(() {
_loadState = ImageLoadState.loading;
_errorMessage = null;
});
}
try {
final imageProvider = NetworkImage(widget.imageUrl);
final completer = Completer<ImageLoadState>();
// 监听图片加载状态
imageProvider.resolve(const ImageConfiguration()).addListener(
ImageStreamListener(
(ImageInfo info, bool synchronousCall) {
if (mounted) {
setState(() {
_loadState = ImageLoadState.success;
});
widget.onSuccess?.call();
completer.complete(ImageLoadState.success);
}
},
onError: (dynamic exception, StackTrace? stackTrace) {
if (mounted) {
setState(() {
_loadState = ImageLoadState.error;
_errorMessage = exception.toString();
});
widget.onError?.call();
completer.completeError(exception);
}
},
),
);
// 支持超时设置
if (widget.timeout != null) {
await completer.future.timeout(
widget.timeout!,
onTimeout: () {
throw TimeoutException('图片加载超时');
},
);
} else {
await completer.future;
}
} catch (e) {
if (mounted) {
setState(() {
_loadState = ImageLoadState.error;
_errorMessage = e.toString();
});
}
}
}
@override
Widget build(BuildContext context) {
return switch (_loadState) {
ImageLoadState.loading => widget.loadingWidget ?? _buildDefaultLoadingWidget(),
ImageLoadState.success => Image.network(
widget.imageUrl,
fit: BoxFit.cover,
errorBuilder: widget.errorWidget != null
? (context, error, stack) => widget.errorWidget!
: null,
),
ImageLoadState.error => widget.errorWidget ?? _buildDefaultErrorWidget(),
ImageLoadState.cancelled => Container(
color: Colors.grey.shade200,
child: const Center(
child: Text('加载已取消'),
),
),
};
}
Widget _buildDefaultLoadingWidget() {
return Container(
color: Colors.grey.shade200,
child: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 8),
Text('加载中...', style: TextStyle(color: Colors.grey)),
],
),
),
);
}
Widget _buildDefaultErrorWidget() {
return Container(
color: Colors.grey.shade300,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error, color: Colors.red, size: 48),
const SizedBox(height: 8),
const Text('图片加载失败', style: TextStyle(color: Colors.grey)),
if (_errorMessage != null) ...[
const SizedBox(height: 4),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
_errorMessage!,
style: const TextStyle(color: Colors.red, fontSize: 12),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
const SizedBox(height: 16),
if (widget.onRetry != null)
ElevatedButton(
onPressed: () {
widget.onRetry?.call();
_loadImage();
},
child: const Text('重试'),
),
],
),
),
);
}
}
2. 创建状态管理组件
dart
class ImageLoader extends StatefulWidget {
final String imageUrl;
const ImageLoader({super.key, required this.imageUrl});
@override
State<ImageLoader> createState() => _ImageLoaderState();
}
class _ImageLoaderState extends State<ImageLoader> {
ImageLoadState _loadState = ImageLoadState.loading;
@override
void initState() {
super.initState();
_loadImage();
}
Future<void> _loadImage() async {
final imageProvider = NetworkImage(widget.imageUrl);
final completer = Completer<void>();
imageProvider.resolve(const ImageConfiguration()).addListener(
ImageStreamListener((ImageInfo info, bool synchronousCall) {
if (mounted) {
setState(() {
_loadState = ImageLoadState.success;
});
completer.complete();
}
}, onError: (dynamic exception, StackTrace? stackTrace) {
if (mounted) {
setState(() {
_loadState = ImageLoadState.error;
});
completer.complete();
}
}),
);
await completer.future;
}
@override
Widget build(BuildContext context) {
return switch (_loadState) {
ImageLoadState.loading => _buildLoadingWidget(),
ImageLoadState.success => Image.network(widget.imageUrl),
ImageLoadState.error => _buildErrorWidget(),
};
}
Widget _buildLoadingWidget() {
return Container(
color: Colors.grey.shade200,
child: const Center(
child: CircularProgressIndicator(),
),
);
}
Widget _buildErrorWidget() {
return Container(
color: Colors.grey.shade300,
child: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error, color: Colors.red, size: 48),
SizedBox(height: 8),
Text('图片加载失败', style: TextStyle(color: Colors.grey)),
],
),
),
);
}
}
关键技术点
1. ImageStreamListener 深度解析
ImageStreamListener是Flutter中监听图片加载状态的核心类,它提供了两个关键回调:成功回调和失败回调。通过这个监听器,我们可以精确捕获图片加载的每个阶段。
图片缓存 ImageStreamListener ImageProvider 客户端代码 图片缓存 ImageStreamListener ImageProvider 客户端代码 alt [下载成功] [下载失败] alt [缓存命中] [缓存未命中] resolve(ImageConfiguration) 查找缓存 返回缓存的Image onImageInfo(success) 下载图片 解码图片 onImageInfo(success) onError(failure) 更新UI状态
dart
/// 高级图片状态监听器
class AdvancedImageStreamListener {
final String imageUrl;
final VoidCallback? onLoadingStart;
final VoidCallback? onLoadingProgress;
final VoidCallback? onSuccess;
final ValueChanged<Exception>? onError;
final ValueChanged<double>? onProgressChanged;
ImageStreamListener? _listener;
ImageStream? _imageStream;
AdvancedImageStreamListener({
required this.imageUrl,
this.onLoadingStart,
this.onLoadingProgress,
this.onSuccess,
this.onError,
this.onProgressChanged,
});
/// 开始监听
void startListening(BuildContext context) {
final imageProvider = NetworkImage(imageUrl);
_imageStream = imageProvider.resolve(const ImageConfiguration());
_listener = ImageStreamListener(
(ImageInfo info, bool synchronousCall) {
onSuccess?.call();
},
onChunk: (ImageChunkEvent event) {
if (event.expectedTotalBytes != null) {
final progress = event.cumulativeBytesLoaded /
event.expectedTotalBytes!;
onProgressChanged?.call(progress);
}
onLoadingProgress?.call();
},
onError: (dynamic error, StackTrace? stackTrace) {
final exception = error is Exception
? error
: Exception(error.toString());
onError?.call(exception);
},
);
_imageStream!.addListener(_listener!);
onLoadingStart?.call();
}
/// 停止监听
void stopListening() {
_imageStream?.removeListener(_listener!);
_listener = null;
_imageStream = null;
}
}
2. mounted 生命周期管理
在Flutter中,组件的销毁是异步的,这可能导致在组件已经销毁后还尝试更新状态,从而引发异常。因此,在所有异步操作完成后调用setState前,都需要检查mounted状态。
dart
/// 安全的状态更新混入类
mixin SafeStateSetter<T extends StatefulWidget> on State<T> {
/// 安全的状态更新
void safeSetState(VoidCallback fn) {
if (mounted) {
setState(fn);
}
}
/// 安全的延迟状态更新
Future<void> safeSetStateDelayed(Duration duration, VoidCallback fn) async {
await Future.delayed(duration);
safeSetState(fn);
}
/// 安全的异步状态更新
Future<void> safeSetStateAsync(Future<void> Function() asyncFn) async {
try {
await asyncFn();
} catch (e) {
// 处理异常
debugPrint('SafeStateSetter error: $e');
}
}
}
/// 使用示例
class _ImageLoaderState extends State<ImageLoader>
with SafeStateSetter<ImageLoader> {
Future<void> _loadImage() async {
safeSetState(() {
_loadState = ImageLoadState.loading;
});
// 异步加载...
await _performLoad();
safeSetState(() {
_loadState = ImageLoadState.success;
});
}
}
组件创建
首次build
状态更新
组件销毁
setState完成
异步期间销毁
created
mounted
updating
disposed
mounted = true
可以安全调用setState
mounted = false
禁止调用setState
3. Completer 异步流程控制
Completer是Dart中用于创建和完成Future的工具类,在图片加载场景中特别有用。它允许我们将异步的回调操作转换为基于Future的异步流程,便于进行链式调用和错误处理。
dart
/// 图片加载任务封装
class ImageLoadTask {
final String url;
final Duration? timeout;
final int retryCount;
ImageLoadTask({
required this.url,
this.timeout,
this.retryCount = 3,
});
/// 执行加载任务
Future<ImageLoadResult> execute(BuildContext context) async {
int attempts = 0;
Exception? lastError;
while (attempts < retryCount) {
try {
return await _loadWithTimeout(context);
} catch (e) {
attempts++;
lastError = e is Exception ? e : Exception(e.toString());
if (attempts < retryCount) {
// 指数退避
final delay = Duration(seconds: attempts);
await Future.delayed(delay);
}
}
}
throw lastError ?? Exception('Unknown error');
}
Future<ImageLoadResult> _loadWithTimeout(BuildContext context) async {
final completer = Completer<ImageLoadResult>();
final imageProvider = NetworkImage(url);
final listener = ImageStreamListener(
(ImageInfo info, bool synchronousCall) {
completer.complete(ImageLoadResult.success(info));
},
onChunk: (ImageChunkEvent event) {
// 进度回调
},
onError: (dynamic error, StackTrace? stackTrace) {
completer.completeError(
error is Exception ? error : Exception(error.toString()),
stackTrace,
);
},
);
final imageStream = imageProvider.resolve(
const ImageConfiguration(),
);
imageStream.addListener(listener);
try {
if (timeout != null) {
return await completer.future.timeout(
timeout!,
onTimeout: () {
throw TimeoutException('图片加载超时');
},
);
} else {
return await completer.future;
}
} finally {
imageStream.removeListener(listener);
}
}
}
/// 图片加载结果
class ImageLoadResult {
final ImageLoadState state;
final ImageInfo? imageInfo;
final Exception? error;
ImageLoadResult.success(this.imageInfo)
: state = ImageLoadState.success,
error = null;
ImageLoadResult.failure(this.error)
: state = ImageLoadState.error,
imageInfo = null;
}
4. 状态持久化与缓存
在应用运行过程中,用户可能会多次访问同一张图片。如果每次都重新加载,不仅浪费网络资源,还会影响用户体验。因此,实现状态持久化和智能缓存是必要的。
dart
/// 图片状态缓存管理器
class ImageStateCacheManager {
static final ImageStateCacheManager _instance =
ImageStateCacheManager._internal();
factory ImageStateCacheManager() => _instance;
ImageStateCacheManager._internal();
final Map<String, CachedImageState> _cache = {};
final int _maxCacheSize = 100;
/// 获取缓存的状态
CachedImageState? getState(String url) {
return _cache[url];
}
/// 保存状态到缓存
void saveState(String url, CachedImageState state) {
// LRU 淘汰策略
if (_cache.length >= _maxCacheSize && !_cache.containsKey(url)) {
_evictOldest();
}
_cache[url] = state.copyWith(lastAccessed: DateTime.now());
}
/// 清除缓存
void clearCache() {
_cache.clear();
}
/// 清除特定URL的缓存
void clearUrl(String url) {
_cache.remove(url);
}
/// 清除过期缓存
void clearExpired(Duration maxAge) {
final now = DateTime.now();
_cache.removeWhere((url, state) {
final age = now.difference(state.lastAccessed);
return age > maxAge;
});
}
/// 获取缓存统计信息
CacheStats getStats() {
return CacheStats(
size: _cache.length,
maxSize: _maxCacheSize,
successCount: _cache.values
.where((s) => s.state == ImageLoadState.success)
.length,
errorCount: _cache.values
.where((s) => s.state == ImageLoadState.error)
.length,
);
}
void _evictOldest() {
String? oldestKey;
DateTime? oldestTime;
_cache.forEach((key, state) {
if (oldestTime == null || state.lastAccessed.isBefore(oldestTime!)) {
oldestTime = state.lastAccessed;
oldestKey = key;
}
});
if (oldestKey != null) {
_cache.remove(oldestKey);
}
}
}
/// 缓存的图片状态
class CachedImageState {
final ImageLoadState state;
final String? errorMessage;
final DateTime lastAccessed;
final DateTime createdAt;
final int accessCount;
CachedImageState({
required this.state,
this.errorMessage,
DateTime? lastAccessed,
DateTime? createdAt,
this.accessCount = 0,
}) : lastAccessed = lastAccessed ?? DateTime.now(),
createdAt = createdAt ?? DateTime.now();
CachedImageState copyWith({
ImageLoadState? state,
String? errorMessage,
DateTime? lastAccessed,
DateTime? createdAt,
int? accessCount,
}) {
return CachedImageState(
state: state ?? this.state,
errorMessage: errorMessage ?? this.errorMessage,
lastAccessed: lastAccessed ?? this.lastAccessed,
createdAt: createdAt ?? this.createdAt,
accessCount: accessCount ?? this.accessCount,
);
}
}
/// 缓存统计信息
class CacheStats {
final int size;
final int maxSize;
final int successCount;
final int errorCount;
CacheStats({
required this.size,
required this.maxSize,
required this.successCount,
required this.errorCount,
});
double get usageRate => size / maxSize;
double get successRate => size == 0 ? 0 : successCount / size;
@override
String toString() {
return 'CacheStats(size: $size/$maxSize, '
'success: $successCount, error: $errorCount, '
'usage: ${(usageRate * 100).toStringAsFixed(1)}%)';
}
}
5. 多图片并发加载管理
在实际应用中,往往需要同时加载多张图片,例如图片列表、相册等场景。如果不对并发加载进行控制,可能会导致大量网络请求同时发起,造成性能问题和用户体验下降。
可用槽位
已达上限
成功
失败
图片加载请求
请求队列
并发控制器
执行加载
等待队列
加载结果
返回图片
重试或失败
释放槽位
dart
/// 并发图片加载控制器
class ConcurrentImageLoader {
final int maxConcurrent;
final Queue<_LoadTask> _queue = Queue();
final Set<_LoadTask> _activeTasks = {};
ConcurrentImageLoader({this.maxConcurrent = 3});
/// 添加加载任务
Future<ImageLoadResult> load(String url) {
final completer = Completer<ImageLoadResult>();
final task = _LoadTask(url, completer);
_queue.add(task);
_processQueue();
return completer.future;
}
/// 处理队列
void _processQueue() {
while (_activeTasks.length < maxConcurrent && _queue.isNotEmpty) {
final task = _queue.removeFirst();
_activeTasks.add(task);
_performLoad(task).whenComplete(() {
_activeTasks.remove(task);
_processQueue();
});
}
}
/// 执行加载
Future<void> _performLoad(_LoadTask task) async {
try {
final imageProvider = NetworkImage(task.url);
final completer = Completer<ImageLoadResult>();
final listener = ImageStreamListener(
(ImageInfo info, bool synchronousCall) {
completer.complete(ImageLoadResult.success(info));
},
onError: (dynamic error, StackTrace? stackTrace) {
completer.completeError(error);
},
);
final imageStream = imageProvider.resolve(
const ImageConfiguration(),
);
imageStream.addListener(listener);
final result = await completer.future;
imageStream.removeListener(listener);
task.completer.complete(result);
} catch (e) {
task.completer.completeError(e);
}
}
/// 取消所有任务
void cancelAll() {
for (final task in _activeTasks) {
task.completer.completeError(Exception('Task cancelled'));
}
_activeTasks.clear();
for (final task in _queue) {
task.completer.completeError(Exception('Task cancelled'));
}
_queue.clear();
}
/// 获取当前状态
LoaderStatus getStatus() {
return LoaderStatus(
queueLength: _queue.length,
activeCount: _activeTasks.length,
maxConcurrent: maxConcurrent,
);
}
}
class _LoadTask {
final String url;
final Completer<ImageLoadResult> completer;
_LoadTask(this.url, this.completer);
}
class LoaderStatus {
final int queueLength;
final int activeCount;
final int maxConcurrent;
LoaderStatus({
required this.queueLength,
required this.activeCount,
required this.maxConcurrent,
});
@override
String toString() {
return 'LoaderStatus(active: $activeCount/$maxConcurrent, '
'queue: $queueLength)';
}
}
6. 状态变更通知机制
在复杂的应用中,图片的加载状态可能需要被多个组件监听和处理。实现一个发布-订阅模式的状态变更通知机制,可以让组件间解耦,提高代码的可维护性。
dart
/// 图片状态变更通知器
class ImageStateNotifier extends ChangeNotifier {
final Map<String, ImageLoadState> _states = {};
final Map<String, String> _errorMessages = {};
/// 获取状态
ImageLoadState? getState(String url) {
return _states[url];
}
/// 获取错误信息
String? getErrorMessage(String url) {
return _errorMessages[url];
}
/// 更新状态
void updateState(String url, ImageLoadState state, {String? error}) {
final changed = _states[url] != state;
_states[url] = state;
if (error != null) {
_errorMessages[url] = error;
}
if (changed) {
notifyListeners();
}
}
/// 批量更新状态
void updateStates(Map<String, ImageLoadState> states) {
var changed = false;
states.forEach((url, state) {
if (_states[url] != state) {
_states[url] = state;
changed = true;
}
});
if (changed) {
notifyListeners();
}
}
/// 清除状态
void clearState(String url) {
if (_states.remove(url) != null) {
notifyListeners();
}
}
/// 清除所有状态
void clearAllStates() {
if (_states.isNotEmpty) {
_states.clear();
_errorMessages.clear();
notifyListeners();
}
}
/// 获取所有加载中的URL
List<String> getLoadingUrls() {
return _states.entries
.where((e) => e.value == ImageLoadState.loading)
.map((e) => e.key)
.toList();
}
/// 获取所有失败的URL
List<String> getErrorUrls() {
return _states.entries
.where((e) => e.value == ImageLoadState.error)
.map((e) => e.key)
.toList();
}
}
7. 状态恢复与持久化
应用可能会被系统杀死或用户手动关闭,这时如果能够恢复之前的加载状态,可以提供更好的用户体验。
dart
/// 状态持久化管理器
class ImageStatePersistence {
static const String _keyPrefix = 'image_state_';
late SharedPreferences _prefs;
/// 初始化
Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
}
/// 保存状态
Future<void> saveState(String url, ImageLoadState state, {
String? error,
}) async {
final key = '$_keyPrefix${_hashUrl(url)}';
await _prefs.setInt('${key}_state', state.index);
if (error != null) {
await _prefs.setString('${key}_error', error);
}
await _prefs.setInt('${key}_timestamp', DateTime.now().millisecondsSinceEpoch);
}
/// 加载状态
Future<PersistedImageState?> loadState(String url) async {
final key = '$_keyPrefix${_hashUrl(url)}';
final stateIndex = _prefs.getInt('${key}_state');
if (stateIndex == null) return null;
final error = _prefs.getString('${key}_error');
final timestamp = _prefs.getInt('${key}_timestamp');
return PersistedImageState(
state: ImageLoadState.values[stateIndex],
error: error,
timestamp: timestamp != null
? DateTime.fromMillisecondsSinceEpoch(timestamp)
: null,
);
}
/// 清除状态
Future<void> clearState(String url) async {
final key = '$_keyPrefix${_hashUrl(url)}';
await _prefs.remove('${key}_state');
await _prefs.remove('${key}_error');
await _prefs.remove('${key}_timestamp');
}
/// 清除所有状态
Future<void> clearAll() async {
final keys = _prefs.getKeys()
.where((key) => key.startsWith(_keyPrefix))
.toList();
for (final key in keys) {
await _prefs.remove(key);
}
}
/// URL哈希
int _hashUrl(String url) {
return url.hashCode;
}
}
class PersistedImageState {
final ImageLoadState state;
final String? error;
final DateTime? timestamp;
PersistedImageState({
required this.state,
this.error,
this.timestamp,
});
bool get isExpired {
if (timestamp == null) return false;
final age = DateTime.now().difference(timestamp!);
return age > const Duration(hours: 1);
}
}
最佳实践
- 始终提供加载状态:让用户知道图片正在加载
- 处理错误情况:提供友好的错误提示和重试选项
- 使用占位符:保持布局稳定,避免UI抖动
- 考虑缓存策略:减少重复加载,提升性能
- 添加动画效果:增强用户体验,使加载过程更生动
常见问题
Q1: 如何知道图片加载的具体进度?
A: 使用loadingBuilder可以获取加载进度:
dart
Image.network(
imageUrl,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
final progress = (loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!);
return CircularProgressIndicator(value: progress);
},
)
Q2: 如何避免重复加载同一张图片?
A: Flutter会自动缓存图片,但可以手动控制:
dart
Image.network(
imageUrl,
cacheWidth: 300,
cacheHeight: 200,
)
Q3: 如何在加载失败时显示默认图片?
A: 使用errorBuilder:
dart
Image.network(
imageUrl,
errorBuilder: (context, error, stackTrace) {
return Image.asset('assets/default_image.png');
},
)
总结
Image加载状态管理是提升用户体验的关键技术。通过合理使用ImageStreamListener、状态枚举和setState,可以创建出流畅、友好的图片加载体验。记住要处理好加载中的状态、加载成功和加载失败三种情况,为用户提供清晰的视觉反馈。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net