From 020656ce14dd7b0d5d8e985cddda50120d2d49e9 Mon Sep 17 00:00:00 2001 From: JaffaKetchup Date: Thu, 11 May 2023 11:22:40 +0100 Subject: [PATCH 1/5] Updated to Flutter 3.10 Reworked tile provider implementations to improve performance and meet guidelines Removed 'Delete Old Runs' workflow to prevent deletion of built artifacts --- .github/workflows/delete_old_runs.yml | 16 --- example/lib/pages/network_tile_provider.dart | 6 +- lib/flutter_map.dart | 10 +- lib/src/layer/tile_layer/tile_layer.dart | 19 +-- .../file_providers/tile_provider_html.dart | 15 +++ .../tile_provider_io.dart} | 11 +- .../file_providers/tile_provider_stub.dart | 17 +++ .../tile_provider/file_tile_provider_web.dart | 17 --- .../tile_provider/network_image_provider.dart | 73 ----------- .../network_no_retry_image_provider.dart | 117 ------------------ .../network_providers/image_provider.dart | 78 ++++++++++++ .../network_providers/tile_provider_base.dart | 14 +++ .../network_providers/tile_provider_html.dart | 36 ++++++ .../network_providers/tile_provider_io.dart | 41 ++++++ .../network_providers/tile_provider_stub.dart | 33 +++++ .../tile_provider/tile_provider_io.dart | 102 --------------- .../tile_provider/tile_provider_web.dart | 70 ----------- 17 files changed, 259 insertions(+), 416 deletions(-) delete mode 100644 .github/workflows/delete_old_runs.yml create mode 100644 lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart rename lib/src/layer/tile_layer/tile_provider/{file_tile_provider_io.dart => file_providers/tile_provider_io.dart} (69%) create mode 100644 lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart delete mode 100644 lib/src/layer/tile_layer/tile_provider/file_tile_provider_web.dart delete mode 100644 lib/src/layer/tile_layer/tile_provider/network_image_provider.dart delete mode 100644 lib/src/layer/tile_layer/tile_provider/network_no_retry_image_provider.dart create mode 100644 lib/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart create mode 100644 lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart create mode 100644 lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart create mode 100644 lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart create mode 100644 lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart delete mode 100644 lib/src/layer/tile_layer/tile_provider/tile_provider_io.dart delete mode 100644 lib/src/layer/tile_layer/tile_provider/tile_provider_web.dart diff --git a/.github/workflows/delete_old_runs.yml b/.github/workflows/delete_old_runs.yml deleted file mode 100644 index dddde2d93..000000000 --- a/.github/workflows/delete_old_runs.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Delete Old Workflow Runs -on: - schedule: - - cron: "0 0 * * 1" # Run every Monday - workflow_dispatch: - -jobs: - del_runs: - runs-on: ubuntu-latest - steps: - - uses: Mattraks/delete-workflow-runs@v2 - with: - token: ${{ github.token }} - repository: ${{ github.repository }} - retain_days: 7 - keep_minimum_runs: 0 diff --git a/example/lib/pages/network_tile_provider.dart b/example/lib/pages/network_tile_provider.dart index d4ee8fc09..5e8336c6b 100644 --- a/example/lib/pages/network_tile_provider.dart +++ b/example/lib/pages/network_tile_provider.dart @@ -47,10 +47,10 @@ class NetworkTileProviderPage extends StatelessWidget { padding: const EdgeInsets.all(8), child: Column( children: [ - Padding( - padding: const EdgeInsets.only(top: 8, bottom: 8), + const Padding( + padding: EdgeInsets.only(top: 8, bottom: 8), child: Wrap( - children: const [ + children: [ Text( 'This provider will automatically retry failed requests, unlike the other pages.'), Text( diff --git a/lib/flutter_map.dart b/lib/flutter_map.dart index e4d956e4d..7948d115d 100644 --- a/lib/flutter_map.dart +++ b/lib/flutter_map.dart @@ -40,10 +40,12 @@ export 'package:flutter_map/src/layer/tile_layer/tile_image.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_provider/asset_tile_provider.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -export 'package:flutter_map/src/layer/tile_layer/tile_provider/file_tile_provider_io.dart' - if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/file_tile_provider_web.dart'; -export 'package:flutter_map/src/layer/tile_layer/tile_provider/tile_provider_io.dart' - if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/tile_provider_web.dart'; +export 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart' + if (dart.library.io) 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart' + if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart'; +export 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart' + if (dart.library.io) 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart' + if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_update_event.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_update_transformer.dart'; diff --git a/lib/src/layer/tile_layer/tile_layer.dart b/lib/src/layer/tile_layer/tile_layer.dart index b0f86abc2..ddaeecc01 100644 --- a/lib/src/layer/tile_layer/tile_layer.dart +++ b/lib/src/layer/tile_layer/tile_layer.dart @@ -19,8 +19,9 @@ import 'package:flutter_map/src/layer/tile_layer/tile_display.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_image.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_image_manager.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/tile_provider_io.dart' - if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/tile_provider_web.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart' + if (dart.library.io) 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart' + if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range_calculator.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_scale_calculator.dart'; @@ -117,17 +118,19 @@ class TileLayer extends StatefulWidget { /// Provider with which to load map tiles /// - /// The default is [NetworkNoRetryTileProvider]. Alternatively, use - /// [NetworkTileProvider] for a network provider which will retry requests. + /// The default is [NetworkTileProvider] which has implementations for both IO + /// and web platforms. /// - /// Both network providers will use some form of caching, although not reliable. For - /// better options, see https://docs.fleaflet.dev/usage/layers/tile-layer#caching. + /// Does not automatically cache (past Flutter's [ImageCache]). For options to + /// add offline mapping, see + /// https://docs.fleaflet.dev/tile-servers/offline-mapping. /// /// `userAgentPackageName` is a construction parameter, which should be passed /// the application's correct package name, such as 'com.example.app'. If no /// value is passed, it defaults to 'unknown'. This parameter is used to form /// part of the 'User-Agent' header, which is important to avoid blocking by - /// tile servers. Namely, the header is the following 'flutter_map ()'. + /// tile servers. Namely, the header is in the following format: + /// 'flutter_map ()'. /// /// Header rules are as follows, after 'User-Agent' is generated as above: /// @@ -280,7 +283,7 @@ class TileLayer extends StatefulWidget { ? const {} : Map.from(additionalOptions), tileProvider = tileProvider == null - ? NetworkNoRetryTileProvider( + ? NetworkTileProvider( headers: {'User-Agent': 'flutter_map ($userAgentPackageName)'}, ) : (tileProvider diff --git a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart new file mode 100644 index 000000000..a2659651f --- /dev/null +++ b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart @@ -0,0 +1,15 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart'; + +/// [TileProvider] to fetch tiles from the local filesystem (not asset store) +/// +/// This web implmentation does not support reading from the local filesystem, +/// and therefore resorts to the web implementation of [NetworkTileProvider]. +class FileTileProvider extends TileProvider { + @override + ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => + NetworkTileProvider().getImage(coordinates, options); +} diff --git a/lib/src/layer/tile_layer/tile_provider/file_tile_provider_io.dart b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart similarity index 69% rename from lib/src/layer/tile_layer/tile_provider/file_tile_provider_io.dart rename to lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart index 88978688b..09a625d45 100644 --- a/lib/src/layer/tile_layer/tile_provider/file_tile_provider_io.dart +++ b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart @@ -5,12 +5,11 @@ import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -/// [TileProvider] that uses [FileImage] internally on platforms other than web +/// [TileProvider] to fetch tiles from the local filesystem (not asset store) +/// +/// Uses [FileImage] internally. class FileTileProvider extends TileProvider { - FileTileProvider(); - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) { - return FileImage(File(getTileUrl(coordinates, options))); - } + ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => + FileImage(File(getTileUrl(coordinates, options))); } diff --git a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart new file mode 100644 index 000000000..3e7381a8c --- /dev/null +++ b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart @@ -0,0 +1,17 @@ +import 'package:flutter/rendering.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart'; + +/// [TileProvider] to fetch tiles from the local filesystem (not asset store) +/// +/// Stub for IO & web specific implementations. +/// +/// The web implmentation does not support reading from the local filesystem, and +/// therefore resorts to the web implementation of [NetworkTileProvider]. +class FileTileProvider extends TileProvider { + @override + ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => + throw UnimplementedError(); +} diff --git a/lib/src/layer/tile_layer/tile_provider/file_tile_provider_web.dart b/lib/src/layer/tile_layer/tile_provider/file_tile_provider_web.dart deleted file mode 100644 index f7225c8f4..000000000 --- a/lib/src/layer/tile_layer/tile_provider/file_tile_provider_web.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; - -/// [TileProvider] that uses [NetworkImage] internally on the web -/// -/// Note that this is not recommended, as important headers cannot be passed. -/// Use [NetworkNoRetryTileProvider] if you know the platform is the web. -class FileTileProvider extends TileProvider { - FileTileProvider(); - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) { - return NetworkImage(getTileUrl(coordinates, options)); - } -} diff --git a/lib/src/layer/tile_layer/tile_provider/network_image_provider.dart b/lib/src/layer/tile_layer/tile_provider/network_image_provider.dart deleted file mode 100644 index 883303d35..000000000 --- a/lib/src/layer/tile_layer/tile_provider/network_image_provider.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/painting.dart'; -import 'package:http/http.dart' as http; -import 'package:http/retry.dart'; - -class FMNetworkImageProvider extends ImageProvider { - /// The URL from which the image will be fetched. - final String url; - - /// The fallback URL from which the image will be fetched. - final String? fallbackUrl; - - /// The http client that is used for the requests. Defaults to a [RetryClient] - /// with a [http.Client]. - final http.Client httpClient; - - /// Custom headers to add to the image fetch request - final Map headers; - - FMNetworkImageProvider( - this.url, { - required this.fallbackUrl, - http.Client? httpClient, - this.headers = const {}, - }) : httpClient = httpClient ?? RetryClient(http.Client()); - - @override - ImageStreamCompleter loadBuffer( - FMNetworkImageProvider key, DecoderBufferCallback decode) { - return OneFrameImageStreamCompleter(_loadWithRetry(key, decode), - informationCollector: () sync* { - yield ErrorDescription('Image provider: $this'); - yield ErrorDescription('Image key: $key'); - }); - } - - @override - Future obtainKey(ImageConfiguration configuration) { - return SynchronousFuture(this); - } - - Future _loadWithRetry( - FMNetworkImageProvider key, - DecoderBufferCallback decode, [ - bool useFallback = false, - ]) async { - assert(key == this); - assert(useFallback == false || fallbackUrl != null); - - try { - final uri = Uri.parse(useFallback ? fallbackUrl! : url); - final response = await httpClient.get(uri, headers: headers); - - if (response.statusCode != 200) { - throw NetworkImageLoadException( - statusCode: response.statusCode, uri: uri); - } - - final codec = - await decode(await ImmutableBuffer.fromUint8List(response.bodyBytes)); - final image = (await codec.getNextFrame()).image; - - return ImageInfo(image: image); - } catch (e) { - if (!useFallback && fallbackUrl != null) { - return _loadWithRetry(key, decode, true); - } - rethrow; - } - } -} diff --git a/lib/src/layer/tile_layer/tile_provider/network_no_retry_image_provider.dart b/lib/src/layer/tile_layer/tile_provider/network_no_retry_image_provider.dart deleted file mode 100644 index 0dc3e5f57..000000000 --- a/lib/src/layer/tile_layer/tile_provider/network_no_retry_image_provider.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/painting.dart'; - -class FMNetworkNoRetryImageProvider - extends ImageProvider { - /// A valid URL, which is the location of the image to be fetched - final String url; - - /// The fallback URL from which the image will be fetched. - final String? fallbackUrl; - - /// The client which will be used to fetch the image - final HttpClient httpClient; - - /// Custom headers to add to the image fetch request - final Map headers; - - FMNetworkNoRetryImageProvider( - this.url, { - required this.fallbackUrl, - HttpClient? httpClient, - this.headers = const {}, - }) : httpClient = httpClient ?? HttpClient() - ..userAgent = null; - - @override - ImageStreamCompleter loadBuffer( - FMNetworkNoRetryImageProvider key, - DecoderBufferCallback decode, - ) { - //ignore: close_sinks - final StreamController chunkEvents = - StreamController(); - - return MultiFrameImageStreamCompleter( - codec: _loadAsync(key: key, decode: decode, chunkEvents: chunkEvents), - chunkEvents: chunkEvents.stream, - scale: 1, - debugLabel: key.url, - informationCollector: () => [ - DiagnosticsProperty('Image provider', this), - DiagnosticsProperty('Image key', key), - ], - ); - } - - @override - Future obtainKey( - ImageConfiguration configuration) { - return SynchronousFuture(this); - } - - Future _loadAsync({ - required FMNetworkNoRetryImageProvider key, - required DecoderBufferCallback decode, - required StreamController chunkEvents, - bool useFallback = false, - }) async { - try { - assert(key == this); - assert(useFallback == false || fallbackUrl != null); - - final Uri resolved = - Uri.base.resolve(useFallback ? key.fallbackUrl! : key.url); - - final HttpClientRequest request = await httpClient.getUrl(resolved); - - headers.forEach((String name, String value) { - request.headers.add(name, value); - }); - - final HttpClientResponse response = await request.close(); - if (response.statusCode != HttpStatus.ok) { - await response.drain>([]); - throw NetworkImageLoadException( - statusCode: response.statusCode, uri: resolved); - } - - final Uint8List bytes = await consolidateHttpClientResponseBytes( - response, - onBytesReceived: (int cumulative, int? total) { - chunkEvents.add(ImageChunkEvent( - cumulativeBytesLoaded: cumulative, - expectedTotalBytes: total, - )); - }, - ); - if (bytes.lengthInBytes == 0) { - throw Exception('NetworkImage is an empty file: $resolved'); - } - - return decode(await ImmutableBuffer.fromUint8List(bytes)); - } catch (e) { - if (!useFallback && fallbackUrl != null) { - return _loadAsync( - key: key, - decode: decode, - chunkEvents: chunkEvents, - useFallback: true, - ); - } - - scheduleMicrotask(() { - _ambiguate(_ambiguate(PaintingBinding.instance)?.imageCache) - ?.evict(key); - }); - chunkEvents.close(); - rethrow; - } - } - - T? _ambiguate(T? value) => value; -} diff --git a/lib/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart b/lib/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart new file mode 100644 index 000000000..c7a277a19 --- /dev/null +++ b/lib/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart @@ -0,0 +1,78 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/painting.dart'; +import 'package:http/http.dart'; + +/// Dedicated [ImageProvider] to fetch tiles from the network +class FlutterMapNetworkImageProvider + extends ImageProvider { + /// The URL to fetch the tile from (GET request) + final String url; + + /// The URL to fetch the tile from (GET request), in the event the original + /// [url] request fails + final String? fallbackUrl; + + /// The HTTP client to use to make network requests + final BaseClient httpClient; + + /// The headers to include with the tile fetch request + final Map headers; + + /// Dedicated [ImageProvider] to fetch tiles from the network + FlutterMapNetworkImageProvider({ + required this.url, + required this.fallbackUrl, + required this.headers, + required this.httpClient, + }); + + @override + ImageStreamCompleter loadImage( + FlutterMapNetworkImageProvider key, + ImageDecoderCallback decode, + ) { + final StreamController chunkEvents = + StreamController(); + + return MultiFrameImageStreamCompleter( + codec: _loadAsync(key, chunkEvents, decode), + chunkEvents: chunkEvents.stream, + scale: 1, + debugLabel: url, + informationCollector: () => [ + DiagnosticsProperty('URL', url), + DiagnosticsProperty('Fallback URL', fallbackUrl), + DiagnosticsProperty('Current provider', key), + ], + ); + } + + @override + Future obtainKey( + ImageConfiguration configuration, + ) => + SynchronousFuture(this); + + Future _loadAsync( + FlutterMapNetworkImageProvider key, + StreamController chunkEvents, + ImageDecoderCallback decode, { + bool useFallback = false, + }) async { + final Uint8List bytes; + try { + bytes = await httpClient.readBytes( + Uri.parse(useFallback ? fallbackUrl ?? '' : url), + headers: headers, + ); + } catch (_) { + if (useFallback) rethrow; + return _loadAsync(key, chunkEvents, decode, useFallback: true); + } + + return decode(await ImmutableBuffer.fromUint8List(bytes)); + } +} diff --git a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart new file mode 100644 index 000000000..31e97dc3c --- /dev/null +++ b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart @@ -0,0 +1,14 @@ +import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; +import 'package:http/http.dart'; +import 'package:http/retry.dart'; +import 'package:meta/meta.dart'; + +@internal +abstract class NetworkTileProviderBase extends TileProvider { + NetworkTileProviderBase({ + super.headers = const {}, + BaseClient? httpClient, + }) : httpClient = httpClient ?? RetryClient(Client()); + + final BaseClient httpClient; +} diff --git a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart new file mode 100644 index 000000000..4e2f6162c --- /dev/null +++ b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart @@ -0,0 +1,36 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart'; + +import 'package:http/retry.dart'; + +/// [TileProvider] to fetch tiles from the network +/// +/// By default, a [RetryClient] is used to retry failed requests. +/// +/// This web implmentation does not support changing the 'User-Agent' header, due +/// to a Dart/browser limitation. +class NetworkTileProvider extends NetworkTileProviderBase { + /// [TileProvider] to fetch tiles from the network + /// + /// By default, a [RetryClient] is used to retry failed requests. + /// + /// This web implmentation does not support changing the 'User-Agent' header, + /// due to a Dart/browser limitation. + NetworkTileProvider({ + super.headers = const {}, + super.httpClient, + }); + + @override + ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => + FlutterMapNetworkImageProvider( + url: getTileUrl(coordinates, options), + fallbackUrl: getTileFallbackUrl(coordinates, options), + headers: headers, + httpClient: httpClient, + ); +} diff --git a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart new file mode 100644 index 000000000..46b5cfd55 --- /dev/null +++ b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart @@ -0,0 +1,41 @@ +import 'dart:io'; + +import 'package:flutter/widgets.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart'; +import 'package:http/retry.dart'; + +/// [TileProvider] to fetch tiles from the network +/// +/// By default, a [RetryClient] is used to retry failed requests. +class NetworkTileProvider extends NetworkTileProviderBase { + /// [TileProvider] that uses [FlutterMapNetworkImageProvider] internally + /// + /// By default, a [RetryClient] is used to retry failed requests. + NetworkTileProvider({ + super.headers = const {}, + super.httpClient, + }); + + @override + ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => + HttpOverrides.runZoned( + () => FlutterMapNetworkImageProvider( + url: getTileUrl(coordinates, options), + fallbackUrl: getTileFallbackUrl(coordinates, options), + headers: headers, + httpClient: httpClient, + ), + createHttpClient: (c) => _FlutterMapHTTPOverrides().createHttpClient(c), + ); +} + +class _FlutterMapHTTPOverrides extends HttpOverrides { + @override + HttpClient createHttpClient(SecurityContext? context) { + return super.createHttpClient(context)..userAgent = null; + } +} diff --git a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart new file mode 100644 index 000000000..f408b95b3 --- /dev/null +++ b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart @@ -0,0 +1,33 @@ +import 'package:flutter/rendering.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart'; +import 'package:http/retry.dart'; + +/// [TileProvider] to fetch tiles from the network +/// +/// Stub for IO & web specific implementations for [NetworkTileProviderBase]. +/// +/// In both implemenations, by default, a [RetryClient] is used to retry failed +/// requests. +/// +/// The web implmentation does not support changing the 'User-Agent' header, due +/// to a Dart/browser limitation. +class NetworkTileProvider extends NetworkTileProviderBase { + /// [TileProvider] to fetch tiles from the network + /// + /// Stub for IO & web specific implementations for [NetworkTileProviderBase]. + /// + /// In both implemenations, by default, a [RetryClient] is used to retry failed + /// requests. + /// + /// The web implmentation does not support changing the 'User-Agent' header, + /// due to a Dart/browser limitation. + NetworkTileProvider({ + super.headers = const {}, + super.httpClient, + }); + + @override + ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => + throw UnimplementedError(); +} diff --git a/lib/src/layer/tile_layer/tile_provider/tile_provider_io.dart b/lib/src/layer/tile_layer/tile_provider/tile_provider_io.dart deleted file mode 100644 index 9cccddacd..000000000 --- a/lib/src/layer/tile_layer/tile_provider/tile_provider_io.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/widgets.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_image_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_no_retry_image_provider.dart'; -import 'package:http/http.dart' as http; -import 'package:http/retry.dart'; - -/// [TileProvider] that uses [FMNetworkImageProvider] internally -/// -/// This image provider automatically retries some failed requests up to 3 -/// times. -/// -/// Note that this provider may be slower than [NetworkNoRetryTileProvider] when -/// fetching tiles due to internal reasons. -/// -/// Note that the 'User-Agent' header and the [RetryClient] cannot be changed, -/// on the web platform. -class NetworkTileProvider extends TileProvider { - NetworkTileProvider({ - Map? headers, - http.Client? httpClient, - }) : httpClient = httpClient ?? RetryClient(http.Client()) { - this.headers = headers ?? {}; - } - - final http.Client httpClient; - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => - HttpOverrides.runZoned( - () => FMNetworkImageProvider( - getTileUrl(coordinates, options), - fallbackUrl: getTileFallbackUrl(coordinates, options), - headers: headers, - httpClient: httpClient, - ), - createHttpClient: (c) => _FlutterMapHTTPOverrides().createHttpClient(c), - ); -} - -/// [TileProvider] that uses [FMNetworkNoRetryImageProvider] internally -/// -/// This image provider does not automatically retry any failed requests. This -/// provider is the default and the recommended provider, unless your tile -/// server is especially unreliable. -/// -/// Note that the 'User-Agent' header and the [HttpClient] cannot be changed, on -/// the web platform. -class NetworkNoRetryTileProvider extends TileProvider { - NetworkNoRetryTileProvider({ - Map? headers, - HttpClient? httpClient, - }) { - this.headers = headers ?? {}; - this.httpClient = httpClient ?? HttpClient() - ..userAgent = null; - } - - late final HttpClient httpClient; - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => - FMNetworkNoRetryImageProvider( - getTileUrl(coordinates, options), - fallbackUrl: getTileFallbackUrl(coordinates, options), - headers: headers, - httpClient: httpClient, - ); -} - -/// A very basic [TileProvider] implementation, that can be extended to create -/// your own provider -/// -/// Using this method is not recommended any more, except for very simple custom -/// [TileProvider]s. Instead, visit the online documentation at -/// https://docs.fleaflet.dev/plugins/making-a-plugin/creating-new-tile-providers. -class CustomTileProvider extends TileProvider { - final String Function(TileCoordinates coors, TileLayer options) customTileUrl; - - CustomTileProvider({required this.customTileUrl}); - - @override - String getTileUrl(TileCoordinates coordinates, TileLayer options) { - return customTileUrl(coordinates, options); - } - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) { - return AssetImage(getTileUrl(coordinates, options)); - } -} - -class _FlutterMapHTTPOverrides extends HttpOverrides { - @override - HttpClient createHttpClient(SecurityContext? context) { - return super.createHttpClient(context)..userAgent = null; - } -} diff --git a/lib/src/layer/tile_layer/tile_provider/tile_provider_web.dart b/lib/src/layer/tile_layer/tile_provider/tile_provider_web.dart deleted file mode 100644 index 20c3b9cc6..000000000 --- a/lib/src/layer/tile_layer/tile_provider/tile_provider_web.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_image_provider.dart'; -import 'package:http/http.dart' as http; - -/// [TileProvider] that uses [FMNetworkImageProvider] internally -/// -/// This image provider automatically retries some failed requests up to 3 times. -/// -/// Note that this provider may be slower than [NetworkNoRetryTileProvider] when fetching tiles due to internal reasons. -/// -/// Note that the 'User-Agent' header and the `RetryClient` cannot be changed, on the web platform. -class NetworkTileProvider extends TileProvider { - NetworkTileProvider({ - Map? headers, - }) { - this.headers = headers ?? {}; - } - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => - FMNetworkImageProvider( - getTileUrl(coordinates, options), - fallbackUrl: getTileFallbackUrl(coordinates, options), - headers: headers..remove('User-Agent'), - ); -} - -/// [TileProvider] that uses [FMNetworkImageProvider] internally with no retry. -/// -/// This image provider does not automatically retry any failed requests. This provider is the default and the recommended provider, unless your tile server is especially unreliable. -/// -/// Note that the 'User-Agent' header and the `HttpClient` cannot be changed, on the web platform. -class NetworkNoRetryTileProvider extends TileProvider { - NetworkNoRetryTileProvider({ - Map? headers, - }) { - this.headers = headers ?? {}; - } - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => - FMNetworkImageProvider( - getTileUrl(coordinates, options), - fallbackUrl: getTileFallbackUrl(coordinates, options), - headers: headers..remove('User-Agent'), - httpClient: http.Client(), - ); -} - -/// A very basic [TileProvider] implementation, that can be extended to create your own provider -/// -/// Using this method is not recommended any more, except for very simple custom [TileProvider]s. Instead, visit the online documentation at https://docs.fleaflet.dev/plugins/making-a-plugin/creating-new-tile-providers. -class CustomTileProvider extends TileProvider { - final String Function(TileCoordinates coors, TileLayer options) customTileUrl; - - CustomTileProvider({required this.customTileUrl}); - - @override - String getTileUrl(TileCoordinates coordinates, TileLayer options) { - return customTileUrl(coordinates, options); - } - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) { - return AssetImage(getTileUrl(coordinates, options)); - } -} From 8346fac3214cb9604e3b8093239216e060516075 Mon Sep 17 00:00:00 2001 From: JaffaKetchup Date: Thu, 11 May 2023 11:49:57 +0100 Subject: [PATCH 2/5] Re-added support for <3.10 --- example/lib/pages/network_tile_provider.dart | 8 ++- .../network_providers/image_provider.dart | 67 +++++++++++++++---- pubspec.yaml | 2 +- 3 files changed, 60 insertions(+), 17 deletions(-) diff --git a/example/lib/pages/network_tile_provider.dart b/example/lib/pages/network_tile_provider.dart index 5e8336c6b..dfa9e4877 100644 --- a/example/lib/pages/network_tile_provider.dart +++ b/example/lib/pages/network_tile_provider.dart @@ -47,10 +47,12 @@ class NetworkTileProviderPage extends StatelessWidget { padding: const EdgeInsets.all(8), child: Column( children: [ - const Padding( - padding: EdgeInsets.only(top: 8, bottom: 8), + // ignore: prefer_const_constructors + Padding( + padding: const EdgeInsets.only(top: 8, bottom: 8), + // ignore: prefer_const_constructors child: Wrap( - children: [ + children: const [ Text( 'This provider will automatically retry failed requests, unlike the other pages.'), Text( diff --git a/lib/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart b/lib/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart index c7a277a19..13edd2a0d 100644 --- a/lib/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart +++ b/lib/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart @@ -29,20 +29,26 @@ class FlutterMapNetworkImageProvider required this.httpClient, }); + // TODO: These [load] and [loadBuffer] method ensure support for Flutter 3.3 + // thru 3.10, hence the multiple deprecation ignorances. Once the methods & + // types have been removed, they need to be replaced with [loadImage], and the + // min SDK constraint will need to be bumped. + @override - ImageStreamCompleter loadImage( + ImageStreamCompleter load( FlutterMapNetworkImageProvider key, - ImageDecoderCallback decode, + // ignore: deprecated_member_use + DecoderCallback decode, ) { final StreamController chunkEvents = StreamController(); return MultiFrameImageStreamCompleter( - codec: _loadAsync(key, chunkEvents, decode), + codec: _loadAsync(key, chunkEvents, decodeDepreacted: decode), chunkEvents: chunkEvents.stream, scale: 1, - debugLabel: url, - informationCollector: () => [ + debugLabel: key.url, + informationCollector: () => [ DiagnosticsProperty('URL', url), DiagnosticsProperty('Fallback URL', fallbackUrl), DiagnosticsProperty('Current provider', key), @@ -51,15 +57,34 @@ class FlutterMapNetworkImageProvider } @override - Future obtainKey( - ImageConfiguration configuration, - ) => - SynchronousFuture(this); + ImageStreamCompleter loadBuffer( + FlutterMapNetworkImageProvider key, + // ignore: deprecated_member_use + DecoderBufferCallback decode, + ) { + final StreamController chunkEvents = + StreamController(); + + return MultiFrameImageStreamCompleter( + codec: _loadAsync(key, chunkEvents, decode: decode), + chunkEvents: chunkEvents.stream, + scale: 1, + debugLabel: key.url, + informationCollector: () => [ + DiagnosticsProperty('URL', url), + DiagnosticsProperty('Fallback URL', fallbackUrl), + DiagnosticsProperty('Current provider', key), + ], + ); + } Future _loadAsync( FlutterMapNetworkImageProvider key, - StreamController chunkEvents, - ImageDecoderCallback decode, { + StreamController chunkEvents, { + // ignore: deprecated_member_use + DecoderBufferCallback? decode, + // ignore: deprecated_member_use + DecoderCallback? decodeDepreacted, bool useFallback = false, }) async { final Uint8List bytes; @@ -70,9 +95,25 @@ class FlutterMapNetworkImageProvider ); } catch (_) { if (useFallback) rethrow; - return _loadAsync(key, chunkEvents, decode, useFallback: true); + return _loadAsync( + key, + chunkEvents, + decode: decode, + decodeDepreacted: decodeDepreacted, + useFallback: true, + ); } - return decode(await ImmutableBuffer.fromUint8List(bytes)); + if (decode != null) { + return decode(await ImmutableBuffer.fromUint8List(bytes)); + } else { + return decodeDepreacted!(bytes); + } } + + @override + Future obtainKey( + ImageConfiguration configuration, + ) => + SynchronousFuture(this); } diff --git a/pubspec.yaml b/pubspec.yaml index 75aaaa5fe..ee5907c37 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ issue_tracker: https://github.com/fleaflet/flutter_map/issues documentation: https://docs.fleaflet.dev environment: - sdk: ">=2.18.0 <3.0.0" + sdk: ">=2.18.0 <4.0.0" flutter: ">=3.3.0" dependencies: From 7c5088bd76de636e26415a63971f83088fdcb7a1 Mon Sep 17 00:00:00 2001 From: JaffaKetchup Date: Thu, 11 May 2023 12:34:44 +0100 Subject: [PATCH 3/5] Removed unnecessary differentiated IO & web `NetworkTileProvider` implementations --- lib/flutter_map.dart | 4 +- lib/src/layer/tile_layer/tile_layer.dart | 41 +++++++++---------- .../file_providers/tile_provider_html.dart | 2 +- .../file_providers/tile_provider_stub.dart | 2 +- ...vider.dart => network_image_provider.dart} | 0 .../network_providers/tile_provider_base.dart | 14 ------- .../network_providers/tile_provider_html.dart | 36 ---------------- .../network_providers/tile_provider_io.dart | 41 ------------------- .../network_providers/tile_provider_stub.dart | 33 --------------- .../tile_provider/network_tile_provider.dart | 38 +++++++++++++++++ 10 files changed, 60 insertions(+), 151 deletions(-) rename lib/src/layer/tile_layer/tile_provider/{network_providers/image_provider.dart => network_image_provider.dart} (100%) delete mode 100644 lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart delete mode 100644 lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart delete mode 100644 lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart delete mode 100644 lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart create mode 100644 lib/src/layer/tile_layer/tile_provider/network_tile_provider.dart diff --git a/lib/flutter_map.dart b/lib/flutter_map.dart index 7948d115d..57d56461b 100644 --- a/lib/flutter_map.dart +++ b/lib/flutter_map.dart @@ -43,9 +43,7 @@ export 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provide export 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart' if (dart.library.io) 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart' if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart'; -export 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart' - if (dart.library.io) 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart' - if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart'; +export 'package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_update_event.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_update_transformer.dart'; diff --git a/lib/src/layer/tile_layer/tile_layer.dart b/lib/src/layer/tile_layer/tile_layer.dart index ddaeecc01..4ba49fde0 100644 --- a/lib/src/layer/tile_layer/tile_layer.dart +++ b/lib/src/layer/tile_layer/tile_layer.dart @@ -18,16 +18,17 @@ import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_display.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_image.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_image_manager.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/asset_tile_provider.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart' - if (dart.library.io) 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart' - if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range_calculator.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_scale_calculator.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_update_event.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_update_transformer.dart'; import 'package:flutter_map/src/map/flutter_map_state.dart'; +import 'package:http/retry.dart'; part 'tile_layer_options.dart'; @@ -118,36 +119,32 @@ class TileLayer extends StatefulWidget { /// Provider with which to load map tiles /// - /// The default is [NetworkTileProvider] which has implementations for both IO - /// and web platforms. + /// The default is [NetworkTileProvider] which supports both IO and web + /// platforms. It uses a [RetryClient] to retry failed requests, but that can + /// be overriden by specifying [NetworkTileProvider.httpClient]. /// /// Does not automatically cache (past Flutter's [ImageCache]). For options to /// add offline mapping, see /// https://docs.fleaflet.dev/tile-servers/offline-mapping. /// - /// `userAgentPackageName` is a construction parameter, which should be passed - /// the application's correct package name, such as 'com.example.app'. If no - /// value is passed, it defaults to 'unknown'. This parameter is used to form - /// part of the 'User-Agent' header, which is important to avoid blocking by - /// tile servers. Namely, the header is in the following format: - /// 'flutter_map ()'. - /// - /// Header rules are as follows, after 'User-Agent' is generated as above: - /// - /// * If no provider is specified here, the default will be used with - /// 'User-Agent' header injected (recommended) - /// * If a provider is specified here with no 'User-Agent' header, that - /// provider will be used and the 'User-Agent' header will be injected - /// * If a provider is specified here with a 'User-Agent' header, that - /// provider will be used and the 'User-Agent' header will not be changed to any created here + /// `userAgentPackageName` is a [TileLayer] parameter, which should be passed + /// the application's correct package name, such as 'com.example.app'. This is + /// important to avoid blocking by tile servers due to high-levels of + /// unidentified traffic. This is passed through to the [NetworkTileProvider] + /// in a suitably formatted string, where it forms the 'User-Agent' header, + /// overriding any custom user agent specified in the HTTP client. To override + /// this behaviour, specify a 'User-Agent' key in the + /// [NetworkTileProvider.headers] property. If no value is passed, it defaults + /// to 'unknown'. This is all ignored on the web, where the 'User-Agent' header + /// cannot be changed due to a limitation of Dart/browsers. /// /// [AssetTileProvider] and [FileTileProvider] are alternatives to network /// providers, which use the [urlTemplate] as a path instead. /// For example, 'assets/map/{z}/{x}/{y}.png' or /// '/storage/emulated/0/map_app/tiles/{z}/{x}/{y}.png'. /// - /// Custom [TileProvider]s can also be used, but these will not follow the header - /// rules above. + /// Custom [TileProvider]s can also be used, but these will not necessarily + /// follow the header rules above. final TileProvider tileProvider; /// When panning the map, keep this many rows and columns of tiles before diff --git a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart index a2659651f..cb7a9577b 100644 --- a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart +++ b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart @@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart'; /// [TileProvider] to fetch tiles from the local filesystem (not asset store) /// diff --git a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart index 3e7381a8c..881a5128c 100644 --- a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart +++ b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart @@ -2,7 +2,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart'; /// [TileProvider] to fetch tiles from the local filesystem (not asset store) /// diff --git a/lib/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart b/lib/src/layer/tile_layer/tile_provider/network_image_provider.dart similarity index 100% rename from lib/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart rename to lib/src/layer/tile_layer/tile_provider/network_image_provider.dart diff --git a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart deleted file mode 100644 index 31e97dc3c..000000000 --- a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -import 'package:http/http.dart'; -import 'package:http/retry.dart'; -import 'package:meta/meta.dart'; - -@internal -abstract class NetworkTileProviderBase extends TileProvider { - NetworkTileProviderBase({ - super.headers = const {}, - BaseClient? httpClient, - }) : httpClient = httpClient ?? RetryClient(Client()); - - final BaseClient httpClient; -} diff --git a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart deleted file mode 100644 index 4e2f6162c..000000000 --- a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_html.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart'; - -import 'package:http/retry.dart'; - -/// [TileProvider] to fetch tiles from the network -/// -/// By default, a [RetryClient] is used to retry failed requests. -/// -/// This web implmentation does not support changing the 'User-Agent' header, due -/// to a Dart/browser limitation. -class NetworkTileProvider extends NetworkTileProviderBase { - /// [TileProvider] to fetch tiles from the network - /// - /// By default, a [RetryClient] is used to retry failed requests. - /// - /// This web implmentation does not support changing the 'User-Agent' header, - /// due to a Dart/browser limitation. - NetworkTileProvider({ - super.headers = const {}, - super.httpClient, - }); - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => - FlutterMapNetworkImageProvider( - url: getTileUrl(coordinates, options), - fallbackUrl: getTileFallbackUrl(coordinates, options), - headers: headers, - httpClient: httpClient, - ); -} diff --git a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart deleted file mode 100644 index 46b5cfd55..000000000 --- a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_io.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/widgets.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/image_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart'; -import 'package:http/retry.dart'; - -/// [TileProvider] to fetch tiles from the network -/// -/// By default, a [RetryClient] is used to retry failed requests. -class NetworkTileProvider extends NetworkTileProviderBase { - /// [TileProvider] that uses [FlutterMapNetworkImageProvider] internally - /// - /// By default, a [RetryClient] is used to retry failed requests. - NetworkTileProvider({ - super.headers = const {}, - super.httpClient, - }); - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => - HttpOverrides.runZoned( - () => FlutterMapNetworkImageProvider( - url: getTileUrl(coordinates, options), - fallbackUrl: getTileFallbackUrl(coordinates, options), - headers: headers, - httpClient: httpClient, - ), - createHttpClient: (c) => _FlutterMapHTTPOverrides().createHttpClient(c), - ); -} - -class _FlutterMapHTTPOverrides extends HttpOverrides { - @override - HttpClient createHttpClient(SecurityContext? context) { - return super.createHttpClient(context)..userAgent = null; - } -} diff --git a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart b/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart deleted file mode 100644 index f408b95b3..000000000 --- a/lib/src/layer/tile_layer/tile_provider/network_providers/tile_provider_stub.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/rendering.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_providers/tile_provider_base.dart'; -import 'package:http/retry.dart'; - -/// [TileProvider] to fetch tiles from the network -/// -/// Stub for IO & web specific implementations for [NetworkTileProviderBase]. -/// -/// In both implemenations, by default, a [RetryClient] is used to retry failed -/// requests. -/// -/// The web implmentation does not support changing the 'User-Agent' header, due -/// to a Dart/browser limitation. -class NetworkTileProvider extends NetworkTileProviderBase { - /// [TileProvider] to fetch tiles from the network - /// - /// Stub for IO & web specific implementations for [NetworkTileProviderBase]. - /// - /// In both implemenations, by default, a [RetryClient] is used to retry failed - /// requests. - /// - /// The web implmentation does not support changing the 'User-Agent' header, - /// due to a Dart/browser limitation. - NetworkTileProvider({ - super.headers = const {}, - super.httpClient, - }); - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => - throw UnimplementedError(); -} diff --git a/lib/src/layer/tile_layer/tile_provider/network_tile_provider.dart b/lib/src/layer/tile_layer/tile_provider/network_tile_provider.dart new file mode 100644 index 000000000..420e9b256 --- /dev/null +++ b/lib/src/layer/tile_layer/tile_provider/network_tile_provider.dart @@ -0,0 +1,38 @@ +import 'package:flutter/rendering.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_image_provider.dart'; +import 'package:http/http.dart'; +import 'package:http/retry.dart'; + +/// [TileProvider] to fetch tiles from the network +/// +/// By default, a [RetryClient] is used to retry failed requests. 'dart:http' +/// or 'dart:io' might be needed to override this. +/// +/// On the web, the 'User-Agent' header cannot be changed as specified in +/// [TileLayer.tileProvider]'s documentation, due to a Dart/browser limitation. +class NetworkTileProvider extends TileProvider { + /// [TileProvider] to fetch tiles from the network + /// + /// By default, a [RetryClient] is used to retry failed requests. 'dart:http' + /// or 'dart:io' might be needed to override this. + /// + /// On the web, the 'User-Agent' header cannot be changed as specified in + /// [TileLayer.tileProvider]'s documentation, due to a Dart/browser limitation. + NetworkTileProvider({ + super.headers = const {}, + BaseClient? httpClient, + }) : httpClient = httpClient ?? RetryClient(Client()); + + /// The HTTP client used to make network requests for tiles + final BaseClient httpClient; + + @override + ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => + FlutterMapNetworkImageProvider( + url: getTileUrl(coordinates, options), + fallbackUrl: getTileFallbackUrl(coordinates, options), + headers: headers, + httpClient: httpClient, + ); +} From 9362a47e048a02051e1cf8e28abf1da76524641c Mon Sep 17 00:00:00 2001 From: JaffaKetchup Date: Thu, 11 May 2023 12:45:07 +0100 Subject: [PATCH 4/5] Fixed GH Actions trigger Updated example app's 'build.gradle' --- .github/workflows/main.yml | 7 ++++++- example/android/build.gradle | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 362eaa629..02db81370 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,10 @@ name: Analyse, Test & Build -on: [push, pull_request, workflow_dispatch] +on: + push: + branches: + - master + pull_request: + workflow_dispatch: jobs: score-package: diff --git a/example/android/build.gradle b/example/android/build.gradle index 2ed02f9aa..b5ae9fa3f 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } From 2da5fe93927a262373a4e83a75bc0281a691420f Mon Sep 17 00:00:00 2001 From: JaffaKetchup Date: Thu, 11 May 2023 15:45:07 +0100 Subject: [PATCH 5/5] Removed support for Flutter <3.10 Removed fallback to `NetworkTileProvider` when `FileTileProvider` used on web --- example/lib/pages/network_tile_provider.dart | 8 +-- example/pubspec.yaml | 6 +- lib/flutter_map.dart | 3 +- .../file_providers/tile_provider_html.dart | 15 ----- .../file_providers/tile_provider_io.dart | 5 ++ .../file_providers/tile_provider_stub.dart | 16 +++-- .../tile_provider/network_image_provider.dart | 67 ++++--------------- pubspec.yaml | 4 +- 8 files changed, 39 insertions(+), 85 deletions(-) delete mode 100644 lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart diff --git a/example/lib/pages/network_tile_provider.dart b/example/lib/pages/network_tile_provider.dart index dfa9e4877..5e8336c6b 100644 --- a/example/lib/pages/network_tile_provider.dart +++ b/example/lib/pages/network_tile_provider.dart @@ -47,12 +47,10 @@ class NetworkTileProviderPage extends StatelessWidget { padding: const EdgeInsets.all(8), child: Column( children: [ - // ignore: prefer_const_constructors - Padding( - padding: const EdgeInsets.only(top: 8, bottom: 8), - // ignore: prefer_const_constructors + const Padding( + padding: EdgeInsets.only(top: 8, bottom: 8), child: Wrap( - children: const [ + children: [ Text( 'This provider will automatically retry failed requests, unlike the other pages.'), Text( diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 9234876f7..7f3e0afc5 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,11 +1,11 @@ name: flutter_map_example description: Example application for 'flutter_map' package publish_to: "none" -version: 4.0.0 +version: 5.0.0 environment: - sdk: ">=2.18.0 <3.0.0" - flutter: ">=3.3.0" + sdk: ">=2.18.0 <4.0.0" + flutter: ">=3.10.0" dependencies: flutter: diff --git a/lib/flutter_map.dart b/lib/flutter_map.dart index 57d56461b..c319cbb67 100644 --- a/lib/flutter_map.dart +++ b/lib/flutter_map.dart @@ -41,8 +41,7 @@ export 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_provider/asset_tile_provider.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart' - if (dart.library.io) 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart' - if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart'; + if (dart.library.io) 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_update_event.dart'; export 'package:flutter_map/src/layer/tile_layer/tile_update_transformer.dart'; diff --git a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart deleted file mode 100644 index cb7a9577b..000000000 --- a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart'; - -/// [TileProvider] to fetch tiles from the local filesystem (not asset store) -/// -/// This web implmentation does not support reading from the local filesystem, -/// and therefore resorts to the web implementation of [NetworkTileProvider]. -class FileTileProvider extends TileProvider { - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => - NetworkTileProvider().getImage(coordinates, options); -} diff --git a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart index 09a625d45..2ee26a312 100644 --- a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart +++ b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart @@ -9,6 +9,11 @@ import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provide /// /// Uses [FileImage] internally. class FileTileProvider extends TileProvider { + /// [TileProvider] to fetch tiles from the local filesystem (not asset store) + /// + /// Uses [FileImage] internally. + FileTileProvider(); + @override ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => FileImage(File(getTileUrl(coordinates, options))); diff --git a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart index 881a5128c..bbfb1ba46 100644 --- a/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart +++ b/lib/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart @@ -2,16 +2,24 @@ import 'package:flutter/rendering.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart'; -import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart'; /// [TileProvider] to fetch tiles from the local filesystem (not asset store) /// /// Stub for IO & web specific implementations. /// -/// The web implmentation does not support reading from the local filesystem, and -/// therefore resorts to the web implementation of [NetworkTileProvider]. +/// This web platform does not support reading from the local filesystem, and +/// therefore throws an [UnsupportedError] when [getImage] is invoked. class FileTileProvider extends TileProvider { + /// [TileProvider] to fetch tiles from the local filesystem (not asset store) + /// + /// Stub for IO & web specific implementations. + /// + /// This web platform does not support reading from the local filesystem, and + /// therefore throws an [UnsupportedError] when [getImage] is invoked. + FileTileProvider(); + @override ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => - throw UnimplementedError(); + throw UnsupportedError( + 'The current platform does not have access to IO (the local filesystem), and therefore does not support `FileTileProvider`'); } diff --git a/lib/src/layer/tile_layer/tile_provider/network_image_provider.dart b/lib/src/layer/tile_layer/tile_provider/network_image_provider.dart index 13edd2a0d..c7a277a19 100644 --- a/lib/src/layer/tile_layer/tile_provider/network_image_provider.dart +++ b/lib/src/layer/tile_layer/tile_provider/network_image_provider.dart @@ -29,26 +29,20 @@ class FlutterMapNetworkImageProvider required this.httpClient, }); - // TODO: These [load] and [loadBuffer] method ensure support for Flutter 3.3 - // thru 3.10, hence the multiple deprecation ignorances. Once the methods & - // types have been removed, they need to be replaced with [loadImage], and the - // min SDK constraint will need to be bumped. - @override - ImageStreamCompleter load( + ImageStreamCompleter loadImage( FlutterMapNetworkImageProvider key, - // ignore: deprecated_member_use - DecoderCallback decode, + ImageDecoderCallback decode, ) { final StreamController chunkEvents = StreamController(); return MultiFrameImageStreamCompleter( - codec: _loadAsync(key, chunkEvents, decodeDepreacted: decode), + codec: _loadAsync(key, chunkEvents, decode), chunkEvents: chunkEvents.stream, scale: 1, - debugLabel: key.url, - informationCollector: () => [ + debugLabel: url, + informationCollector: () => [ DiagnosticsProperty('URL', url), DiagnosticsProperty('Fallback URL', fallbackUrl), DiagnosticsProperty('Current provider', key), @@ -57,34 +51,15 @@ class FlutterMapNetworkImageProvider } @override - ImageStreamCompleter loadBuffer( - FlutterMapNetworkImageProvider key, - // ignore: deprecated_member_use - DecoderBufferCallback decode, - ) { - final StreamController chunkEvents = - StreamController(); - - return MultiFrameImageStreamCompleter( - codec: _loadAsync(key, chunkEvents, decode: decode), - chunkEvents: chunkEvents.stream, - scale: 1, - debugLabel: key.url, - informationCollector: () => [ - DiagnosticsProperty('URL', url), - DiagnosticsProperty('Fallback URL', fallbackUrl), - DiagnosticsProperty('Current provider', key), - ], - ); - } + Future obtainKey( + ImageConfiguration configuration, + ) => + SynchronousFuture(this); Future _loadAsync( FlutterMapNetworkImageProvider key, - StreamController chunkEvents, { - // ignore: deprecated_member_use - DecoderBufferCallback? decode, - // ignore: deprecated_member_use - DecoderCallback? decodeDepreacted, + StreamController chunkEvents, + ImageDecoderCallback decode, { bool useFallback = false, }) async { final Uint8List bytes; @@ -95,25 +70,9 @@ class FlutterMapNetworkImageProvider ); } catch (_) { if (useFallback) rethrow; - return _loadAsync( - key, - chunkEvents, - decode: decode, - decodeDepreacted: decodeDepreacted, - useFallback: true, - ); + return _loadAsync(key, chunkEvents, decode, useFallback: true); } - if (decode != null) { - return decode(await ImmutableBuffer.fromUint8List(bytes)); - } else { - return decodeDepreacted!(bytes); - } + return decode(await ImmutableBuffer.fromUint8List(bytes)); } - - @override - Future obtainKey( - ImageConfiguration configuration, - ) => - SynchronousFuture(this); } diff --git a/pubspec.yaml b/pubspec.yaml index ee5907c37..2d9250cc4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,14 +1,14 @@ name: flutter_map description: A versatile mapping package for Flutter, based off leaflet.js, that's simple and easy to learn, yet completely customizable and configurable. -version: 4.0.0 +version: 5.0.0 repository: https://github.com/fleaflet/flutter_map issue_tracker: https://github.com/fleaflet/flutter_map/issues documentation: https://docs.fleaflet.dev environment: sdk: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" + flutter: ">=3.10.0" dependencies: async: ^2.9.0