背景
在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等功能。