|
| 1 | +import 'dart:async'; |
1 | 2 | import 'dart:ui';
|
2 | 3 |
|
3 | 4 | import 'package:flutter/foundation.dart';
|
4 | 5 | import 'package:flutter/painting.dart';
|
5 |
| -import 'package:http/http.dart' as http; |
6 |
| -import 'package:http/retry.dart'; |
| 6 | +import 'package:http/http.dart'; |
7 | 7 |
|
8 |
| -class FMNetworkImageProvider extends ImageProvider<FMNetworkImageProvider> { |
9 |
| - /// The URL from which the image will be fetched. |
| 8 | +/// Dedicated [ImageProvider] to fetch tiles from the network |
| 9 | +class FlutterMapNetworkImageProvider |
| 10 | + extends ImageProvider<FlutterMapNetworkImageProvider> { |
| 11 | + /// The URL to fetch the tile from (GET request) |
10 | 12 | final String url;
|
11 | 13 |
|
12 |
| - /// The fallback URL from which the image will be fetched. |
| 14 | + /// The URL to fetch the tile from (GET request), in the event the original |
| 15 | + /// [url] request fails |
13 | 16 | final String? fallbackUrl;
|
14 | 17 |
|
15 |
| - /// The http client that is used for the requests. Defaults to a [RetryClient] |
16 |
| - /// with a [http.Client]. |
17 |
| - final http.Client httpClient; |
| 18 | + /// The HTTP client to use to make network requests |
| 19 | + final BaseClient httpClient; |
18 | 20 |
|
19 |
| - /// Custom headers to add to the image fetch request |
| 21 | + /// The headers to include with the tile fetch request |
20 | 22 | final Map<String, String> headers;
|
21 | 23 |
|
22 |
| - FMNetworkImageProvider( |
23 |
| - this.url, { |
| 24 | + /// Dedicated [ImageProvider] to fetch tiles from the network |
| 25 | + FlutterMapNetworkImageProvider({ |
| 26 | + required this.url, |
24 | 27 | required this.fallbackUrl,
|
25 |
| - http.Client? httpClient, |
26 |
| - this.headers = const {}, |
27 |
| - }) : httpClient = httpClient ?? RetryClient(http.Client()); |
| 28 | + required this.headers, |
| 29 | + required this.httpClient, |
| 30 | + }); |
28 | 31 |
|
29 | 32 | @override
|
30 |
| - ImageStreamCompleter loadBuffer( |
31 |
| - FMNetworkImageProvider key, DecoderBufferCallback decode) { |
32 |
| - return OneFrameImageStreamCompleter(_loadWithRetry(key, decode), |
33 |
| - informationCollector: () sync* { |
34 |
| - yield ErrorDescription('Image provider: $this'); |
35 |
| - yield ErrorDescription('Image key: $key'); |
36 |
| - }); |
| 33 | + ImageStreamCompleter loadImage( |
| 34 | + FlutterMapNetworkImageProvider key, |
| 35 | + ImageDecoderCallback decode, |
| 36 | + ) { |
| 37 | + final StreamController<ImageChunkEvent> chunkEvents = |
| 38 | + StreamController<ImageChunkEvent>(); |
| 39 | + |
| 40 | + return MultiFrameImageStreamCompleter( |
| 41 | + codec: _loadAsync(key, chunkEvents, decode), |
| 42 | + chunkEvents: chunkEvents.stream, |
| 43 | + scale: 1, |
| 44 | + debugLabel: url, |
| 45 | + informationCollector: () => [ |
| 46 | + DiagnosticsProperty('URL', url), |
| 47 | + DiagnosticsProperty('Fallback URL', fallbackUrl), |
| 48 | + DiagnosticsProperty('Current provider', key), |
| 49 | + ], |
| 50 | + ); |
37 | 51 | }
|
38 | 52 |
|
39 | 53 | @override
|
40 |
| - Future<FMNetworkImageProvider> obtainKey(ImageConfiguration configuration) { |
41 |
| - return SynchronousFuture<FMNetworkImageProvider>(this); |
42 |
| - } |
| 54 | + Future<FlutterMapNetworkImageProvider> obtainKey( |
| 55 | + ImageConfiguration configuration, |
| 56 | + ) => |
| 57 | + SynchronousFuture<FlutterMapNetworkImageProvider>(this); |
43 | 58 |
|
44 |
| - Future<ImageInfo> _loadWithRetry( |
45 |
| - FMNetworkImageProvider key, |
46 |
| - DecoderBufferCallback decode, [ |
| 59 | + Future<Codec> _loadAsync( |
| 60 | + FlutterMapNetworkImageProvider key, |
| 61 | + StreamController<ImageChunkEvent> chunkEvents, |
| 62 | + ImageDecoderCallback decode, { |
47 | 63 | bool useFallback = false,
|
48 |
| - ]) async { |
49 |
| - assert(key == this); |
50 |
| - assert(useFallback == false || fallbackUrl != null); |
51 |
| - |
| 64 | + }) async { |
| 65 | + final Uint8List bytes; |
52 | 66 | try {
|
53 |
| - final uri = Uri.parse(useFallback ? fallbackUrl! : url); |
54 |
| - final response = await httpClient.get(uri, headers: headers); |
55 |
| - |
56 |
| - if (response.statusCode != 200) { |
57 |
| - throw NetworkImageLoadException( |
58 |
| - statusCode: response.statusCode, uri: uri); |
59 |
| - } |
60 |
| - |
61 |
| - final codec = |
62 |
| - await decode(await ImmutableBuffer.fromUint8List(response.bodyBytes)); |
63 |
| - final image = (await codec.getNextFrame()).image; |
64 |
| - |
65 |
| - return ImageInfo(image: image); |
66 |
| - } catch (e) { |
67 |
| - if (!useFallback && fallbackUrl != null) { |
68 |
| - return _loadWithRetry(key, decode, true); |
69 |
| - } |
70 |
| - rethrow; |
| 67 | + bytes = await httpClient.readBytes( |
| 68 | + Uri.parse(useFallback ? fallbackUrl ?? '' : url), |
| 69 | + headers: headers, |
| 70 | + ); |
| 71 | + } catch (_) { |
| 72 | + if (useFallback) rethrow; |
| 73 | + return _loadAsync(key, chunkEvents, decode, useFallback: true); |
71 | 74 | }
|
| 75 | + |
| 76 | + return decode(await ImmutableBuffer.fromUint8List(bytes)); |
72 | 77 | }
|
73 | 78 | }
|
0 commit comments