Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: improved interface of cancellable tile loading #1941

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions lib/src/layer/tile_layer/tile_image_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,18 @@ class TileImageManager {
final tilesToReload = List<TileImage>.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();
}
}
Expand Down
23 changes: 13 additions & 10 deletions lib/src/layer/tile_layer/tile_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -545,16 +545,19 @@ class _TileLayerState extends State<TileLayer> with TickerProviderStateMixin {
}) {
final cancelLoading = Completer<void>();

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,
Expand Down
132 changes: 51 additions & 81 deletions lib/src/layer/tile_layer/tile_provider/base_tile_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,6 @@ abstract class TileProvider {
/// documentation, due to a Dart/browser limitation.
final Map<String, String> 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
Expand All @@ -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<void> 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
///
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<void> cancelLoading,
);
}
Loading