Flutter cached_network_image使用Dio请求图片

1,847 阅读1分钟

背景

在Flutter中通常使用cached_network_image控件显示图片,该控件使用http库请求图片,http库无法像dio一样方便的添加拦截器等。

实现

CachedNetworkImage使用flutter_cache_manager进行图片缓存,可以使用自定义cache_manager来替换http为dio。 如下:

自定义CacheManager

class DioCacheManager extends CacheManager {
  static const key = 'dioCache';

  static DioCacheManager _instance;

  factory DioCacheManager(Dio dio) {
    if (_instance == null) {
      _instance = DioCacheManager._(dio);
    }
    return _instance;
  }

  DioCacheManager._(Dio dio)
      : super(Config(key, fileService: DioHttpFileService(dio)));
}

自定义FileService

class DioHttpFileService extends FileService {
  Dio _dio;

  DioHttpFileService(this._dio);

  @override
  Future<FileServiceResponse> get(String url,
      {Map<String, String> headers}) async {
    Options options =
        Options(headers: headers ?? {}, responseType: ResponseType.stream);

    Response<ResponseBody> httpResponse =
        await _dio.get<ResponseBody>(url, options: options);

    return DioGetResponse(httpResponse);
  }
}

自定义FileServiceResponse

class DioGetResponse implements FileServiceResponse {
  final DateTime _receivedTime = DateTime.now();
  Response<ResponseBody> _response;

  DioGetResponse(this._response);

  @override
  Stream<List<int>> get content => _response.data.stream;

  @override
  int get contentLength => _getContentLength();

  @override
  String get eTag => _response.headers['etag']?.first ?? '-1';

  @override
  String get fileExtension {
    var fileExtension = '';
    final contentTypeHeader =
        _response.headers[Headers.contentTypeHeader]?.first;
    if (contentTypeHeader != null) {
      final contentType = ContentType.parse(contentTypeHeader);
      fileExtension = mimeTypes[contentType.mimeType];
    }
    return fileExtension;
  }

  @override
  int get statusCode => _response.statusCode;

  @override
  DateTime get validTill {
    // Without a cache-control header we keep the file for a week
    var ageDuration = const Duration(days: 7);
    final controlHeader = _response.headers['cache-control']?.first;
    if (controlHeader != null) {
      final controlSettings = controlHeader.split(',');
      for (final setting in controlSettings) {
        final sanitizedSetting = setting.trim().toLowerCase();
        if (sanitizedSetting == 'no-cache') {
          ageDuration = const Duration();
        }
        if (sanitizedSetting.startsWith('max-age=')) {
          var validSeconds = int.tryParse(sanitizedSetting.split('=')[1]) ?? 0;
          if (validSeconds > 0) {
            ageDuration = Duration(seconds: validSeconds);
          }
        }
      }
    }

    return _receivedTime.add(ageDuration);
  }

  int _getContentLength() {
    try {
      return int.parse(
          _response.headers[Headers.contentLengthHeader]?.first ?? '-1');
    } catch (e) {
      return -1;
    }
  }
}

使用方法

dependencies:
  flutter_cache_manager_dio: ^2.0.0
var dio = Dio();
dio.interceptors.add(LogInterceptor(responseBody: false));
...
CachedNetworkImage(
    cacheManager: DioCacheManager(dio),
    imageUrl: url,
    placeholder: (context, url) => CircularProgressIndicator(),
    errorWidget: (context, url, error) => Container(
    child: Text(
        'error',
        ),
    ),
);

然后就可以使用dio的拦截等功能了,比如使用拦截器添加并刷新token,添加log等功能。

地址

github.com/xfans/flutt…