diff --git a/lib/src/layer/tile_layer/tile_image_manager.dart b/lib/src/layer/tile_layer/tile_image_manager.dart index 986e51e7f..cad622ab9 100644 --- a/lib/src/layer/tile_layer/tile_image_manager.dart +++ b/lib/src/layer/tile_layer/tile_image_manager.dart @@ -118,16 +118,18 @@ class TileImageManager { final tilesToReload = List.from(_tiles.values); for (final tile in tilesToReload) { - tile.imageProvider = layer.tileProvider.supportsCancelLoading - ? layer.tileProvider.getImageWithCancelLoadingSupport( - tileBounds.atZoom(tile.coordinates.z).wrap(tile.coordinates), - layer, - tile.cancelLoading.future, - ) - : layer.tileProvider.getImage( - tileBounds.atZoom(tile.coordinates.z).wrap(tile.coordinates), - layer, - ); + if (layer.tileProvider case final CancellableTileLoadingSupport tp) { + tile.imageProvider = tp.getImageWithCancelLoadingSupport( + tileBounds.atZoom(tile.coordinates.z).wrap(tile.coordinates), + layer, + tile.cancelLoading.future, + ); + } else { + tile.imageProvider = layer.tileProvider.getImage( + tileBounds.atZoom(tile.coordinates.z).wrap(tile.coordinates), + layer, + ); + } tile.load(); } } diff --git a/lib/src/layer/tile_layer/tile_layer.dart b/lib/src/layer/tile_layer/tile_layer.dart index c0b8e19e2..96a8fa462 100644 --- a/lib/src/layer/tile_layer/tile_layer.dart +++ b/lib/src/layer/tile_layer/tile_layer.dart @@ -545,16 +545,19 @@ class _TileLayerState extends State with TickerProviderStateMixin { }) { final cancelLoading = Completer(); - final imageProvider = widget.tileProvider.supportsCancelLoading - ? widget.tileProvider.getImageWithCancelLoadingSupport( - tileBoundsAtZoom.wrap(coordinates), - widget, - cancelLoading.future, - ) - : widget.tileProvider.getImage( - tileBoundsAtZoom.wrap(coordinates), - widget, - ); + late final ImageProvider imageProvider; + if (widget.tileProvider case final CancellableTileLoadingSupport tp) { + imageProvider = tp.getImageWithCancelLoadingSupport( + tileBoundsAtZoom.wrap(coordinates), + widget, + cancelLoading.future, + ); + } else { + imageProvider = widget.tileProvider.getImage( + tileBoundsAtZoom.wrap(coordinates), + widget, + ); + } return TileImage( vsync: this, diff --git a/lib/src/layer/tile_layer/tile_provider/base_tile_provider.dart b/lib/src/layer/tile_layer/tile_provider/base_tile_provider.dart index 954684aa5..f393ffadf 100644 --- a/lib/src/layer/tile_layer/tile_provider/base_tile_provider.dart +++ b/lib/src/layer/tile_layer/tile_provider/base_tile_provider.dart @@ -25,26 +25,6 @@ abstract class TileProvider { /// documentation, due to a Dart/browser limitation. final Map headers; - /// Indicates to flutter_map internals whether to call [getImage] (when - /// `false`) or [getImageWithCancelLoadingSupport] - /// - /// The appropriate method must be overriden, else an [UnimplementedError] - /// will be thrown. - /// - /// [getImageWithCancelLoadingSupport] is designed to allow for implementations - /// that can cancel HTTP requests or other processing in-flight, when the - /// underlying tile is disposed before it is loaded. This may increase - /// performance, and may decrease unnecessary tile requests. Note that this - /// only applies to the web platform. For more information, and detailed - /// implementation expectations, see documentation on - /// [getImageWithCancelLoadingSupport]. - /// - /// [getImage] does not support cancellation. - /// - /// Defaults to `false`. Only needs to be overridden where - /// [getImageWithCancelLoadingSupport] is in use. - bool get supportsCancelLoading => false; - /// Construct the base tile provider and initialise the [headers] property /// /// This is not a constant constructor, and does not use an initialising @@ -63,63 +43,9 @@ abstract class TileProvider { /// Usually redirects to a custom [ImageProvider], with one input depending on /// [getTileUrl]. /// - /// For many implementations, this is the only method that will need - /// implementing. - /// - /// --- - /// - /// Does not support cancelling loading tiles, unlike - /// [getImageWithCancelLoadingSupport]. For this method to be called instead of - /// that, [supportsCancelLoading] must be `false` (default). - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) { - throw UnimplementedError( - 'A `TileProvider` that does not override `supportsCancelLoading` to `true` ' - 'must override `getImage`', - ); - } - - /// Retrieve a tile as an image, based on its coordinates and the [TileLayer] - /// - /// For this method to be called instead of [getImage], [supportsCancelLoading] - /// must be overriden to `true`. - /// - /// Usually redirects to a custom [ImageProvider], with one parameter using - /// [getTileUrl], and one using [cancelLoading]. - /// - /// For many implementations, this is the only method that will need - /// implementing. - /// - /// --- - /// - /// Supports cancelling loading tiles, which is designed to allow for - /// implementations that can cancel HTTP requests or other processing - /// in-flight, when the underlying tile is disposed before it is loaded. This - /// may increase performance, and may decrease unnecessary tile requests. Note - /// that this only applies to the web platform. - /// - /// The [cancelLoading] future will complete when the underlying tile is - /// disposed/pruned. The implementation should therefore listen for its - /// completion, then cancel the loading. If an image [Codec] is required, - /// decode [transparentImage] - it will never be visible anyway. Note that - /// [cancelLoading] will always be completed on disposal, even if the tile has - /// been fully loaded, but this side effect is not usually an issue. - /// - /// See this example with 'package:dio's `CancelToken`: - /// - /// ```dart - /// final cancelToken = CancelToken(); - /// cancelLoading.then((_) => cancelToken.cancel()); - /// ``` - ImageProvider getImageWithCancelLoadingSupport( - TileCoordinates coordinates, - TileLayer options, - Future cancelLoading, - ) { - throw UnimplementedError( - 'A `TileProvider` that overrides `supportsCancelLoading` to `true` must ' - 'override `getImageWithCancelLoadingSupport`', - ); - } + /// Does not support cancelling/aborting the network requests used to fetch a + /// tile. This may be supported by mixing in [CancellableTileLoadingSupport]. + ImageProvider getImage(TileCoordinates coordinates, TileLayer options); /// Called when the [TileLayer] is disposed /// @@ -245,10 +171,6 @@ abstract class TileProvider { } /// [Uint8List] that forms a fully transparent image - /// - /// Intended to be used with [getImageWithCancelLoadingSupport], so that a - /// cancelled tile load returns this. It will not be displayed. An error cannot - /// be thrown from a custom [ImageProvider]. static final transparentImage = Uint8List.fromList([ 0x89, 0x50, @@ -319,3 +241,51 @@ abstract class TileProvider { 0x82, ]); } + +/// Adds cancellation support to a [TileProvider] +/// +/// Implementers should define [getImageWithCancelLoadingSupport] instead of +/// [getImage]. See [getImageWithCancelLoadingSupport] for information. +mixin CancellableTileLoadingSupport on TileProvider { + @override + @nonVirtual + Never getImage(TileCoordinates coordinates, TileLayer options) { + throw UnsupportedError( + '`TileProvider`s that support `CancellableTileLoadingSupport` support ' + 'only `getImageWithCancelLoadingSupport` instead of `getImage`', + ); + } + + /// Retrieve a tile as an image, based on its coordinates and the [TileLayer], + /// and allow cancellation/abortion of the network request used to fetch a + /// tile + /// + /// Usually redirects to a custom [ImageProvider], with one parameter using + /// [getTileUrl], and one using [cancelLoading]. + /// + /// Supports cancelling loading tiles, which is designed to allow for + /// implementations that can cancel HTTP requests or other processing + /// in-flight, when the underlying tile is disposed before it is loaded. This + /// may increase performance, and may decrease unnecessary tile requests. Note + /// that this only applies to the web platform. + /// + /// The [cancelLoading] future will complete when the underlying tile is + /// disposed/pruned. The implementation should therefore listen for its + /// completion, then cancel the loading. If an image [Codec] is required, + /// decode [TileProvider.transparentImage] - it will never be visible anyway. + /// Note that [cancelLoading] will always be completed on disposal, even if + /// the tile has been fully loaded, but it is not safe to assume + /// [cancelLoading] will always be completed. + /// + /// See this example with 'package:dio's `CancelToken`: + /// + /// ```dart + /// final cancelToken = CancelToken(); + /// cancelLoading.then((_) => cancelToken.cancel()); + /// ``` + ImageProvider getImageWithCancelLoadingSupport( + TileCoordinates coordinates, + TileLayer options, + Future cancelLoading, + ); +}