Skip to content
Draft
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
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ analyzer:

linter:
rules:
avoid_slow_async_io: false
avoid_slow_async_io: false
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,20 @@ 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({
required this.icon,
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({
required this.icon,
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;
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
9 changes: 7 additions & 2 deletions jaffa_lints.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ linter:
- one_member_abstracts
- only_throw_errors
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
- parameter_assignments
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -210,4 +215,4 @@ linter:
- use_to_and_as_if_applicable
- use_truncating_division
- valid_regexps
- void_checks
- void_checks
2 changes: 1 addition & 1 deletion lib/src/backend/impls/objectbox/backend/internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ class _ObjectBoxBackendImpl implements FMTCObjectBoxBackendInternal {
@override
Future<Map<String, int>> removeOldestTilesAboveLimit({
required List<String> 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1001,9 +1001,6 @@ Future<void> _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()) {
Expand Down
54 changes: 28 additions & 26 deletions lib/src/providers/image_provider/image_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -46,7 +52,7 @@ class _FMTCImageProvider extends ImageProvider<_FMTCImageProvider> {
MultiFrameImageStreamCompleter(
codec: provideTile(
coords: coords,
options: options,
networkUrl: networkUrl,
provider: provider,
key: key,
finishedLoadingBytes: finishedLoadingBytes,
Expand All @@ -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<Uint8List> provideTile({
required TileCoordinates coords,
required TileLayer options,
required FMTCTileProvider provider,
required String networkUrl,
TileCoordinates? coords,
Object? key,
void Function()? startedLoading,
void Function()? finishedLoadingBytes,
Expand All @@ -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!
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand Down
4 changes: 1 addition & 3 deletions lib/src/providers/image_provider/internal_tile_browser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
part of '../../../flutter_map_tile_caching.dart';

Future<Uint8List> _internalTileBrowser({
required TileCoordinates coords,
required TileLayer options,
required String networkUrl,
required FMTCTileProvider provider,
required bool requireValidImage,
required _TLIRConstructor? currentTLIR,
Expand All @@ -27,7 +26,6 @@ Future<Uint8List> _internalTileBrowser({
}
}

final networkUrl = provider.getTileUrl(coords, options);
final matcherUrl = provider.urlTransformer?.call(networkUrl) ?? networkUrl;

currentTLIR?.networkUrl = networkUrl;
Expand Down
Loading
Loading