Created
November 12, 2022 21:36
-
-
Save ueman/01a335e5aff118a294b69b8c269ac008 to your computer and use it in GitHub Desktop.
http and dio image
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Requires at least Flutter 3.x | |
import 'dart:async'; | |
import 'dart:ui' as ui; | |
import 'package:flutter/foundation.dart'; | |
import 'package:dio/dio.dart'; | |
import 'package:flutter/widgets.dart'; | |
/// 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]. | |
@immutable | |
class DioImage extends ImageProvider<DioImage> { | |
/// 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, Dio? dio}) | |
: dio = dio ?? Dio(); | |
/// 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<String, String>? headers; | |
/// [dio] will be the default [Dio] if not set. | |
final Dio dio; | |
@override | |
Future<DioImage> obtainKey(ImageConfiguration configuration) { | |
return SynchronousFuture<DioImage>(this); | |
} | |
@override | |
ImageStreamCompleter loadBuffer(DioImage key, DecoderBufferCallback 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<ImageChunkEvent>(); | |
return MultiFrameImageStreamCompleter( | |
codec: _loadAsync(key, chunkEvents, decode), | |
chunkEvents: chunkEvents.stream, | |
scale: key.scale, | |
debugLabel: key.url.toString(), | |
informationCollector: () => <DiagnosticsNode>[ | |
DiagnosticsProperty<ImageProvider>('Image provider', this), | |
DiagnosticsProperty<DioImage>('Image key', key), | |
], | |
); | |
} | |
Future<ui.Codec> _loadAsync( | |
DioImage key, | |
StreamController<ImageChunkEvent> chunkEvents, | |
DecoderBufferCallback decode, | |
) async { | |
try { | |
assert(key == this); | |
final response = await dio.getUri( | |
url, | |
options: Options(headers: headers, responseType: ResponseType.bytes), | |
onReceiveProgress: (count, total) { | |
chunkEvents.add(ImageChunkEvent( | |
cumulativeBytesLoaded: count, | |
expectedTotalBytes: total, | |
)); | |
}, | |
); | |
if (response.statusCode != 200) { | |
throw NetworkImageLoadException( | |
statusCode: response.statusCode!, | |
uri: url, | |
); | |
} | |
final bytes = Uint8List.fromList(response.data as List<int>); | |
if (bytes.lengthInBytes == 0) { | |
throw Exception('NetworkImage is an empty file: $url'); | |
} | |
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; | |
} | |
@override | |
int get hashCode => Object.hash(url, scale); | |
@override | |
String toString() => | |
'${objectRuntimeType(this, 'NetworkImage')}("$url", scale: $scale)'; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Requires at least Flutter 3.x | |
import 'dart:async'; | |
import 'dart:ui' as ui; | |
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/widgets.dart'; | |
import 'package:http/http.dart'; | |
/// 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]. | |
@immutable | |
class HttpImage extends ImageProvider<HttpImage> { | |
/// Creates an object that fetches the image at the given URL. | |
/// | |
/// The arguments [url] and [scale] must not be null. | |
/// [client] will be the default [Client] if not set. | |
HttpImage(this.url, {this.scale = 1.0, this.headers, Client? client}) | |
: client = client ?? Client(); | |
/// 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<String, String>? headers; | |
/// [client] will be the default [Client] if not set. | |
final Client client; | |
@override | |
Future<HttpImage> obtainKey(ImageConfiguration configuration) { | |
return SynchronousFuture<HttpImage>(this); | |
} | |
@override | |
ImageStreamCompleter loadBuffer(HttpImage key, DecoderBufferCallback decode) { | |
return MultiFrameImageStreamCompleter( | |
codec: _loadAsync(key as HttpImage, decode), | |
scale: key.scale, | |
debugLabel: key.url.toString(), | |
informationCollector: () => <DiagnosticsNode>[ | |
DiagnosticsProperty<ImageProvider>('Image provider', this), | |
DiagnosticsProperty<HttpImage>('Image key', key), | |
], | |
); | |
} | |
Future<ui.Codec> _loadAsync( | |
HttpImage key, | |
DecoderBufferCallback decode, | |
) async { | |
try { | |
assert(key == this); | |
final response = await client.get(url, headers: headers); | |
if (response.statusCode != 200) { | |
throw NetworkImageLoadException( | |
statusCode: response.statusCode, uri: url); | |
} | |
final bytes = response.bodyBytes; | |
if (bytes.lengthInBytes == 0) { | |
throw Exception('HttpImage is an empty file: $url'); | |
} | |
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; | |
} | |
} | |
@override | |
bool operator ==(Object other) { | |
if (other.runtimeType != runtimeType) { | |
return false; | |
} | |
return other is HttpImage && other.url == url && other.scale == scale; | |
} | |
@override | |
int get hashCode => Object.hash(url, scale); | |
@override | |
String toString() => | |
'${objectRuntimeType(this, 'HttpImage')}("$url", scale: $scale)'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment