blob: 86349f15c0d0fc45f94658bea1b8c20a75f889fd [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@immutable
class CustomNetworkImage extends ImageProvider<Uri> {
const CustomNetworkImage(this.url);
final String url;
@override
Future<Uri> obtainKey(ImageConfiguration configuration) {
final Uri result = Uri.parse(url).replace(
queryParameters: <String, String>{
'dpr': '${configuration.devicePixelRatio}',
'locale': '${configuration.locale?.toLanguageTag()}',
'platform': '${configuration.platform?.name}',
'width': '${configuration.size?.width}',
'height': '${configuration.size?.height}',
'bidi': '${configuration.textDirection?.name}',
},
);
return SynchronousFuture<Uri>(result);
}
static HttpClient get _httpClient {
HttpClient? client;
assert(() {
if (debugNetworkImageHttpClientProvider != null) {
client = debugNetworkImageHttpClientProvider!();
}
return true;
}());
return client ?? HttpClient()..autoUncompress = false;
}
@override
ImageStreamCompleter loadImage(Uri key, ImageDecoderCallback decode) {
final StreamController<ImageChunkEvent> chunkEvents = StreamController<ImageChunkEvent>();
debugPrint('Fetching "$key"...');
return MultiFrameImageStreamCompleter(
codec: _httpClient.getUrl(key)
.then<HttpClientResponse>((HttpClientRequest request) => request.close())
.then<Uint8List>((HttpClientResponse response) {
return consolidateHttpClientResponseBytes(
response,
onBytesReceived: (int cumulative, int? total) {
chunkEvents.add(ImageChunkEvent(
cumulativeBytesLoaded: cumulative,
expectedTotalBytes: total,
));
},
);
})
.catchError((Object e, StackTrace stack) {
scheduleMicrotask(() {
PaintingBinding.instance.imageCache.evict(key);
});
return Future<Uint8List>.error(e, stack);
})
.whenComplete(chunkEvents.close)
.then<ui.ImmutableBuffer>(ui.ImmutableBuffer.fromUint8List)
.then<ui.Codec>(decode),
chunkEvents: chunkEvents.stream,
scale: 1.0,
debugLabel: '"key"',
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty<ImageProvider>('Image provider', this),
DiagnosticsProperty<Uri>('URL', key),
],
);
}
@override
String toString() => '${objectRuntimeType(this, 'CustomNetworkImage')}("$url")';
}
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Image(
image: const CustomNetworkImage('https://flutter.github.io/assets-for-api-docs/assets/widgets/flamingos.jpg'),
width: constraints.hasBoundedWidth ? constraints.maxWidth : null,
height: constraints.hasBoundedHeight ? constraints.maxHeight : null,
);
},
),
);
}
}