diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d4d04e2..921eeec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,13 @@ Many thanks to my sponsors, no matter how much or how little they donated. Spons # Changelog -## [10.1.1] - 2025/02/03 +## [11.0.0-dev.2] - 2025/03/09 + +* Change `FMTCTileProvider.provideTile` arguments + Require a tile's URL & optional coordinates; instead of required coordinates and required `TileLayer` +* Fixed overly-aggressive Flutter-side tile image caching which prevented changes to `TileLayer.urlTemplate` from updating the displayed tiles + +## [10.1.1] - 2025/03/09 * Fixed bug where import operation fatally crashed on some iOS devices This appears to be an [ObjectBox issue](https://github.com/objectbox/objectbox-dart/issues/654) where streaming the results of a database query caused the crash. Instead, FMTC now uses a custom chunking system to avoid streaming and also avoid loading potentially many tiles into memory. diff --git a/analysis_options.yaml b/analysis_options.yaml index 97dbfab7..59d8a4a8 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -6,4 +6,4 @@ analyzer: linter: rules: - avoid_slow_async_io: false \ No newline at end of file + avoid_slow_async_io: false diff --git a/example/lib/src/screens/main/map_view/components/download_progress/download_progress_masker.dart b/example/lib/src/screens/main/map_view/components/download_progress/download_progress_masker.dart index 597cc019..baad5862 100644 --- a/example/lib/src/screens/main/map_view/components/download_progress/download_progress_masker.dart +++ b/example/lib/src/screens/main/map_view/components/download_progress/download_progress_masker.dart @@ -28,7 +28,7 @@ class DownloadProgressMasker extends StatefulWidget { final int minZoom; final int maxZoom; final int tileSize; - final TileLayer child; + final Widget child; // To reset after a download, the `key` must be changed diff --git a/example/lib/src/screens/main/secondary_view/contents/region_selection/components/shape_selector/components/animated_visibility_icon_button.dart b/example/lib/src/screens/main/secondary_view/contents/region_selection/components/shape_selector/components/animated_visibility_icon_button.dart index a4821bfa..40d64fb2 100644 --- a/example/lib/src/screens/main/secondary_view/contents/region_selection/components/shape_selector/components/animated_visibility_icon_button.dart +++ b/example/lib/src/screens/main/secondary_view/contents/region_selection/components/shape_selector/components/animated_visibility_icon_button.dart @@ -6,8 +6,6 @@ class _AnimatedVisibilityIconButton extends StatelessWidget { this.onPressed, this.tooltip, required this.isVisible, - // This is exactly what we want to do - // ignore: avoid_field_initializers_in_const_classes }) : _mode = 0; const _AnimatedVisibilityIconButton.filledTonal({ @@ -15,8 +13,6 @@ class _AnimatedVisibilityIconButton extends StatelessWidget { this.onPressed, this.tooltip, required this.isVisible, - // This is exactly what we want to do - // ignore: avoid_field_initializers_in_const_classes }) : _mode = 1; const _AnimatedVisibilityIconButton.filled({ @@ -24,8 +20,6 @@ class _AnimatedVisibilityIconButton extends StatelessWidget { this.onPressed, this.tooltip, required this.isVisible, - // This is exactly what we want to do - // ignore: avoid_field_initializers_in_const_classes }) : _mode = 2; final Icon icon; diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 9d00b756..85079493 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,7 +1,7 @@ name: fmtc_demo description: The demo app for 'flutter_map_tile_caching', showcasing its functionality and use-cases. publish_to: "none" -version: 10.1.1 +version: 11.0.0 environment: sdk: ">=3.6.0 <4.0.0" diff --git a/jaffa_lints.yaml b/jaffa_lints.yaml index 81dfd034..10204f2f 100644 --- a/jaffa_lints.yaml +++ b/jaffa_lints.yaml @@ -102,7 +102,6 @@ linter: - one_member_abstracts - only_throw_errors - overridden_fields - - package_api_docs - package_names - package_prefixed_library_names - parameter_assignments @@ -155,6 +154,7 @@ linter: - sort_constructors_first - sort_pub_dependencies - sort_unnamed_constructors_first + - strict_top_level_inference - test_types_in_equals - throw_in_finally - tighten_type_of_initializing_formals @@ -163,12 +163,14 @@ linter: - type_literal_in_constant_pattern - unawaited_futures - unintended_html_in_doc_comment + - unnecessary_async - unnecessary_await_in_return - unnecessary_brace_in_string_interps - unnecessary_breaks - unnecessary_const - unnecessary_constructor_name - unnecessary_getters_setters + - unnecessary_ignore - unnecessary_lambdas - unnecessary_late - unnecessary_library_directive @@ -187,8 +189,10 @@ linter: - unnecessary_string_interpolations - unnecessary_this - unnecessary_to_list_in_spreads + - unnecessary_underscores - unreachable_from_main - unrelated_type_equality_checks + - unsafe_variance - use_build_context_synchronously - use_colored_box - use_decorated_box @@ -200,6 +204,7 @@ linter: - use_key_in_widget_constructors - use_late_for_private_fields_and_variables - use_named_constants + - use_null_aware_elements - use_raw_strings - use_rethrow_when_possible - use_setters_to_change_properties @@ -210,4 +215,4 @@ linter: - use_to_and_as_if_applicable - use_truncating_division - valid_regexps - - void_checks \ No newline at end of file + - void_checks diff --git a/lib/src/backend/impls/objectbox/backend/internal.dart b/lib/src/backend/impls/objectbox/backend/internal.dart index b6e63090..aa5be6aa 100644 --- a/lib/src/backend/impls/objectbox/backend/internal.dart +++ b/lib/src/backend/impls/objectbox/backend/internal.dart @@ -461,7 +461,7 @@ class _ObjectBoxBackendImpl implements FMTCObjectBoxBackendInternal { @override Future> removeOldestTilesAboveLimit({ required List storeNames, - }) async { + }) { // By sharing a single completer, all invocations of this method during the // debounce period will return the same result at the same time if (_rotalResultCompleter?.isCompleted ?? true) { diff --git a/lib/src/backend/impls/objectbox/backend/internal_workers/standard/worker.dart b/lib/src/backend/impls/objectbox/backend/internal_workers/standard/worker.dart index 3737946d..a660d32f 100644 --- a/lib/src/backend/impls/objectbox/backend/internal_workers/standard/worker.dart +++ b/lib/src/backend/impls/objectbox/backend/internal_workers/standard/worker.dart @@ -1001,9 +1001,6 @@ Future _worker( id: cmd.id, data: {'numExportedTiles': numExportedTiles}, ); - - // We don't care what type, we always need to clean up and rethrow - // ignore: avoid_catches_without_on_clauses } catch (e) { exportingRoot.close(); if (workingDir.existsSync()) { diff --git a/lib/src/providers/image_provider/image_provider.dart b/lib/src/providers/image_provider/image_provider.dart index bdad0f15..b11f48dd 100644 --- a/lib/src/providers/image_provider/image_provider.dart +++ b/lib/src/providers/image_provider/image_provider.dart @@ -10,22 +10,28 @@ class _FMTCImageProvider extends ImageProvider<_FMTCImageProvider> { /// Create a specialised [ImageProvider] that uses FMTC internals to enable /// browse caching const _FMTCImageProvider({ - required this.provider, - required this.options, + required this.networkUrl, required this.coords, + required this.provider, required this.startedLoading, required this.finishedLoadingBytes, }); - /// An instance of the [FMTCTileProvider] in use - final FMTCTileProvider provider; - - /// An instance of the [TileLayer] in use - final TileLayer options; + /// The network URL of the tile at [coords], determined by + /// [FMTCTileProvider.getTileUrl] + final String networkUrl; /// The coordinates of the tile to be fetched + /// + /// Must be set when using the image provider - acts as a key for + /// [FMTCTileProvider.tileLoadingInterceptor], and is used for some debug + /// info. Optional when [provideTile] is used directly, if + /// `tileLoadingInterceptor` functionality is not used. final TileCoordinates coords; + /// An instance of the [FMTCTileProvider] in use + final FMTCTileProvider provider; + /// Function invoked when the image starts loading (not from cache) /// /// Used with [finishedLoadingBytes] to safely dispose of the `httpClient` @@ -46,7 +52,7 @@ class _FMTCImageProvider extends ImageProvider<_FMTCImageProvider> { MultiFrameImageStreamCompleter( codec: provideTile( coords: coords, - options: options, + networkUrl: networkUrl, provider: provider, key: key, finishedLoadingBytes: finishedLoadingBytes, @@ -55,26 +61,22 @@ class _FMTCImageProvider extends ImageProvider<_FMTCImageProvider> { ).then(ImmutableBuffer.fromUint8List).then((v) => decode(v)), scale: 1, debugLabel: coords.toString(), - informationCollector: () { - final tileUrl = provider.getTileUrl(coords, options); - - return [ - DiagnosticsProperty('Stores', provider.stores), - DiagnosticsProperty('Tile coordinates', coords), - DiagnosticsProperty('Tile URL', tileUrl), - DiagnosticsProperty( - 'Tile storage-suitable UID', - provider.urlTransformer?.call(tileUrl) ?? tileUrl, - ), - ]; - }, + informationCollector: () => [ + DiagnosticsProperty('Stores', provider.stores), + DiagnosticsProperty('Tile coordinates', coords), + DiagnosticsProperty('Tile URL', networkUrl), + DiagnosticsProperty( + 'Tile storage-suitable UID', + provider.urlTransformer?.call(networkUrl) ?? networkUrl, + ), + ], ); /// {@macro fmtc.tileProvider.provideTile} static Future provideTile({ - required TileCoordinates coords, - required TileLayer options, required FMTCTileProvider provider, + required String networkUrl, + TileCoordinates? coords, Object? key, void Function()? startedLoading, void Function()? finishedLoadingBytes, @@ -92,7 +94,7 @@ class _FMTCImageProvider extends ImageProvider<_FMTCImageProvider> { scheduleMicrotask(() => PaintingBinding.instance.imageCache.evict(key)); } - if (currentTLIR != null) { + if (currentTLIR != null && coords != null) { currentTLIR.error = error; provider.tileLoadingInterceptor! @@ -121,8 +123,7 @@ class _FMTCImageProvider extends ImageProvider<_FMTCImageProvider> { final Uint8List bytes; try { bytes = await _internalTileBrowser( - coords: coords, - options: options, + networkUrl: networkUrl, provider: provider, requireValidImage: requireValidImage, currentTLIR: currentTLIR, @@ -150,6 +151,7 @@ class _FMTCImageProvider extends ImageProvider<_FMTCImageProvider> { bool operator ==(Object other) => identical(this, other) || (other is _FMTCImageProvider && + other.networkUrl == networkUrl && other.coords == coords && other.provider == provider); diff --git a/lib/src/providers/image_provider/internal_tile_browser.dart b/lib/src/providers/image_provider/internal_tile_browser.dart index 393242d2..8ff49be2 100644 --- a/lib/src/providers/image_provider/internal_tile_browser.dart +++ b/lib/src/providers/image_provider/internal_tile_browser.dart @@ -4,8 +4,7 @@ part of '../../../flutter_map_tile_caching.dart'; Future _internalTileBrowser({ - required TileCoordinates coords, - required TileLayer options, + required String networkUrl, required FMTCTileProvider provider, required bool requireValidImage, required _TLIRConstructor? currentTLIR, @@ -27,7 +26,6 @@ Future _internalTileBrowser({ } } - final networkUrl = provider.getTileUrl(coords, options); final matcherUrl = provider.urlTransformer?.call(networkUrl) ?? networkUrl; currentTLIR?.networkUrl = networkUrl; diff --git a/lib/src/providers/tile_provider/tile_provider.dart b/lib/src/providers/tile_provider/tile_provider.dart index a183fffb..4117aa5f 100644 --- a/lib/src/providers/tile_provider/tile_provider.dart +++ b/lib/src/providers/tile_provider/tile_provider.dart @@ -278,9 +278,9 @@ class FMTCTileProvider extends TileProvider { @override ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => _FMTCImageProvider( - provider: this, - options: options, + networkUrl: getTileUrl(coordinates, options), coords: coordinates, + provider: this, startedLoading: () => _tilesInProgress[coordinates] = Completer(), finishedLoadingBytes: () { _tilesInProgress[coordinates]?.complete(); @@ -300,9 +300,7 @@ class FMTCTileProvider extends TileProvider { } /// {@template fmtc.tileProvider.provideTile} - /// Use FMTC's caching logic to get the bytes of the specific tile (at - /// [coords]) with the specified [TileLayer] options and [FMTCTileProvider] - /// provider + /// Use FMTC's caching logic to get the bytes of the tile at [networkUrl] /// /// > [!IMPORTANT] /// > Note that this will actuate the cache writing mechanism as if a normal @@ -321,6 +319,10 @@ class FMTCTileProvider extends TileProvider { /// /// --- /// + /// [coords] is required to enable functioning of + /// [FMTCTileProvider.tileLoadingInterceptor]. If the tile loading interceptor + /// is not in use, providing coordinates is not necessary. + /// /// [key] is used to control the [ImageCache], and should be set when in a /// context where [ImageProvider.obtainKey] is available. /// @@ -342,16 +344,16 @@ class FMTCTileProvider extends TileProvider { /// to be decoded (now or at a later time). /// {@endtemplate} Future provideTile({ - required TileCoordinates coords, - required TileLayer options, + required String networkUrl, + TileCoordinates? coords, Object? key, void Function()? startedLoading, void Function()? finishedLoadingBytes, bool requireValidImage = false, }) => _FMTCImageProvider.provideTile( + networkUrl: networkUrl, coords: coords, - options: options, provider: this, key: key, startedLoading: startedLoading, diff --git a/lib/src/root/recovery.dart b/lib/src/root/recovery.dart index 04f5a8ee..cfc8faa7 100644 --- a/lib/src/root/recovery.dart +++ b/lib/src/root/recovery.dart @@ -58,7 +58,7 @@ class RootRecovery { /// /// {@macro fmtc.rootRecovery.failedDefinition} Future> - get recoverableRegions async => + get recoverableRegions => FMTCBackendAccess.internal.listRecoverableRegions().then( (rs) => rs.map( (r) => @@ -79,7 +79,7 @@ class RootRecovery { } /// {@macro fmtc.backend.cancelRecovery} - Future cancel(int id) async => + Future cancel(int id) => FMTCBackendAccess.internal.cancelRecovery(id: id); } diff --git a/pubspec.yaml b/pubspec.yaml index 657345f0..05aa84dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_map_tile_caching description: Plugin for 'flutter_map' providing advanced caching functionality, with ability to download map regions for offline use. -version: 10.1.1 +version: 11.0.0-dev.2 repository: https://github.com/JaffaKetchup/flutter_map_tile_caching issue_tracker: https://github.com/JaffaKetchup/flutter_map_tile_caching/issues diff --git a/windowsApplicationInstallerSetup.iss b/windowsApplicationInstallerSetup.iss index 3f7c58ad..a131ac3d 100644 --- a/windowsApplicationInstallerSetup.iss +++ b/windowsApplicationInstallerSetup.iss @@ -1,7 +1,7 @@ ; Script generated by the Inno Setup Script Wizard #define MyAppName "FMTC Demo" -#define MyAppVersion "for 10.1.1" +#define MyAppVersion "for 11.0.0" #define MyAppPublisher "JaffaKetchup Development" #define MyAppURL "https://github.com/JaffaKetchup/flutter_map_tile_caching" #define MyAppSupportURL "https://github.com/JaffaKetchup/flutter_map_tile_caching/issues"