You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter网络图片加载:设置连接超时、进度及错误占位图方案咨询

解决方案:带超时、加载进度和错误占位的网络图片组件

方案一:自定义ImageProvider(无需第三方库)

不用修改Flutter源码,通过自定义NetworkImage子类实现超时控制,同时结合状态管理实现加载进度和错误占位。

1. 创建带超时的自定义HttpClient

import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

final HttpClient _customHttpClient = HttpClient()
  ..connectionTimeout = const Duration(seconds: 5) // 连接超时
  ..receiveTimeout = const Duration(seconds: 5); // 接收超时

2. 实现带超时的NetworkImage子类

class TimeoutNetworkImage extends NetworkImage {
  final Duration timeout;

  TimeoutNetworkImage(
    super.url, {
    super.scale,
    super.headers,
    this.timeout = const Duration(seconds: 5),
  });

  @override
  ImageStreamCompleter loadImage(NetworkImage key, ImageDecoderCallback decode) {
    return MultiFrameImageStreamCompleter(
      codec: _loadAsync(key, decode),
      scale: key.scale,
      informationCollector: () => [
        DiagnosticsProperty<ImageProvider>('Image provider', this),
        DiagnosticsProperty<NetworkImage>('Image key', key),
      ],
    );
  }

  Future<Codec> _loadAsync(NetworkImage key, ImageDecoderCallback decode) async {
    assert(key == this);
    final Uri resolved = Uri.parse(key.url);

    final HttpClientRequest request = await _customHttpClient.getUrl(resolved);
    headers?.forEach((String name, String value) => request.headers.add(name, value));
    
    // 触发超时逻辑
    final HttpClientResponse response = await request.close();
    if (response.statusCode != HttpStatus.ok) {
      throw NetworkImageLoadException(statusCode: response.statusCode, uri: resolved);
    }

    final Uint8List bytes = await consolidateHttpClientResponseBytes(response);
    if (bytes.isEmpty) throw Exception('Empty network image');

    return decode(bytes);
  }
}

3. 封装成可复用组件(带加载/错误状态)

class NetworkImageWithTimeout extends StatefulWidget {
  final String url;
  final Widget placeholder;
  final Widget errorWidget;
  final Duration timeout;

  const NetworkImageWithTimeout({
    super.key,
    required this.url,
    required this.placeholder,
    required this.errorWidget,
    this.timeout = const Duration(seconds: 5),
  });

  @override
  State<NetworkImageWithTimeout> createState() => _NetworkImageWithTimeoutState();
}

class _NetworkImageWithTimeoutState extends State<NetworkImageWithTimeout> {
  ImageProvider? _image;
  bool _isLoading = true;
  bool _hasError = false;

  @override
  void initState() {
    super.initState();
    _loadImage();
  }

  Future<void> _loadImage() async {
    try {
      final image = TimeoutNetworkImage(widget.url, timeout: widget.timeout);
      // 预加载触发超时检查
      await image.obtainKey(const ImageConfiguration()).then((key) {
        return image.load(key, (bytes) => PaintingBinding.instance.instantiateImageCodec(bytes));
      });
      if (mounted) {
        setState(() {
          _image = image;
          _isLoading = false;
        });
      }
    } catch (_) {
      if (mounted) {
        setState(() {
          _hasError = true;
          _isLoading = false;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) return widget.placeholder;
    if (_hasError) return widget.errorWidget;
    return Image(image: _image!);
  }
}

使用示例

NetworkImageWithTimeout(
  url: 'https://your-image-url.com/image.jpg',
  placeholder: const Center(child: CircularProgressIndicator()),
  errorWidget: const Icon(Icons.error_outline, color: Colors.red, size: 48),
)

方案二:使用第三方库cached_network_image(更简单)

最新版本的cached_network_image支持传入自定义HttpClient,直接实现超时控制,同时内置加载/错误占位功能。

1. 配置自定义HttpClient

HttpClient _createTimeoutHttpClient() {
  return HttpClient()
    ..connectionTimeout = const Duration(seconds: 5)
    ..receiveTimeout = const Duration(seconds: 5);
}

2. 使用组件

CachedNetworkImage(
  imageUrl: 'https://your-image-url.com/image.jpg',
  httpClient: _createTimeoutHttpClient(), // 传入带超时的客户端
  placeholder: (context, url) => const CircularProgressIndicator(),
  errorWidget: (context, url, error) => const Icon(Icons.error),
  // 可选:配置缓存规则
  cacheManager: CacheManager(
    Config(
      'custom_image_cache',
      stalePeriod: const Duration(days: 7),
    ),
  ),
)

关键说明

  • 原生NetworkImage使用全局共享的_sharedHttpClient,无法直接设置超时,自定义子类或第三方库的自定义客户端可以绕过这个限制。
  • 两种方案都能实现5秒超时、加载进度显示和错误占位的需求,方案二更简洁,方案一无需依赖第三方库。

内容的提问来源于stack exchange,提问作者Šubyk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 08:05:19