// Original code from https://github.com/ueman/image_provider/blob/ed6ff43e1ba69a8ab5a3701d70974cae9cd68a34/dio_image_provider/lib/dio_image_provider.dart // Modified by lifegpc import 'dart:async'; import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:dio/dio.dart'; import 'package:flutter/widgets.dart'; import 'package:logging/logging.dart'; import '../globals.dart'; final _log = Logger("DioImageProvider"); /// Fetches the given URL from the network, associating it with the given scale. /// /// The image will be cached regardless of cache headers from the server. /// /// See also: /// /// * [Image.network]. /// * https://pub.dev/packages/http_image_provider @immutable class DioImage extends ImageProvider { static Dio defaultDio = Dio(); /// Creates an object that fetches the image at the given URL. /// /// The arguments [url] and [scale] must not be null. /// [dio] will be the default [Dio] if not set. DioImage.string(String url, {this.scale = 1.0, this.headers, this.onData, Dio? dio, this.key}) : dio = dio ?? defaultDio, url = Uri.parse(url); /// Creates an object that fetches the image at the given URL. /// /// The arguments [url] and [scale] must not be null. /// [dio] will be the default [Dio] if not set. DioImage(this.url, {this.scale = 1.0, this.headers, this.onData, this.key, Dio? dio}) : dio = dio ?? defaultDio; /// The URL from which the image will be fetched. final Uri url; /// The scale to place in the [ImageInfo] object of the image. final double scale; /// The HTTP headers that will be used with [HttpClient.get] to fetch image from network. /// /// When running flutter on the web, headers are not used. final Map? headers; /// [dio] will be the default [Dio] if not set. final Dio dio; final void Function(Uint8List, Headers, String)? onData; final Key? key; @override Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override ImageStreamCompleter loadImage(DioImage key, ImageDecoderCallback decode) { // Ownership of this controller is handed off to [_loadAsync]; it is that // method's responsibility to close the controller's stream when the image // has been loaded or an error is thrown. final chunkEvents = StreamController(); return MultiFrameImageStreamCompleter( codec: _loadAsync(key, chunkEvents, decode), chunkEvents: chunkEvents.stream, scale: key.scale, debugLabel: key.url.toString(), informationCollector: () => [ DiagnosticsProperty('Image provider', this), DiagnosticsProperty('Image key', key), ], ); } Future _loadAsync( DioImage key, StreamController chunkEvents, ImageDecoderCallback decode, ) async { try { assert(key == this); if (isImageCacheEnabled) { try { final cache = await imageCaches.getCache(url.toString()); if (cache != null) { if (onData != null) { onData!(cache!.$1, Headers.fromMap(cache!.$2), cache!.$3 ?? url.toString()); } final buffer = await ui.ImmutableBuffer.fromUint8List(cache!.$1); return decode(buffer); } } catch (e, stack) { _log.warning("Failed to get cache for ${url.toString()}: $e\n$stack"); } } final response = await dio.getUri( url, options: Options(headers: headers, responseType: ResponseType.bytes), onReceiveProgress: (count, total) { chunkEvents.add(ImageChunkEvent( cumulativeBytesLoaded: count, expectedTotalBytes: total >= 0 ? total : null, )); }, ); if (response.statusCode != 200) { throw NetworkImageLoadException( uri: url, statusCode: response.statusCode!, ); } final bytes = Uint8List.fromList(response.data as List); if (bytes.lengthInBytes == 0) { throw NetworkImageLoadException( uri: url, statusCode: response.statusCode!, ); } if (onData != null) { onData!(bytes, response.headers, response.realUri.toString()); } if (isImageCacheEnabled) { try { await imageCaches.putCache(url.toString(), bytes, response.headers.map, response.realUri.toString()); } catch (e, stack) { _log.warning("Failed to put cache for ${url.toString()}: $e\n$stack"); } } final buffer = await ui.ImmutableBuffer.fromUint8List(bytes); return decode(buffer); } catch (e) { // Depending on where the exception was thrown, the image cache may not // have had a chance to track the key in the cache at all. // Schedule a microtask to give the cache a chance to add the key. scheduleMicrotask(() { PaintingBinding.instance.imageCache.evict(key); }); rethrow; } finally { unawaited(chunkEvents.close()); } } @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is DioImage && other.url == url && other.scale == scale && other.key == key; } @override int get hashCode => Object.hash(url, scale, key); @override String toString() => '${objectRuntimeType(this, 'DioImage')}("$url", scale: $scale)'; }