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
2 changes: 2 additions & 0 deletions example/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.builtInKotlin=false
android.newDsl=false
40 changes: 24 additions & 16 deletions lib/src/layer/tile_layer/tile_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class TileImage extends ChangeNotifier {
ImageStream? _imageStream;
late ImageStreamListener _listener;

Timer? _loadDelayTimer;

/// Create a new object for a tile image.
TileImage({
required this.vsync,
Expand Down Expand Up @@ -132,28 +134,33 @@ class TileImage extends ChangeNotifier {
}

/// Initiate loading of the image.
void load() {
if (cancelLoading.isCompleted) return;
Future<void> load({Duration delay = Duration.zero}) async {
void startLoad() {
if (cancelLoading.isCompleted) return;

loadStarted = DateTime.now();
loadStarted = DateTime.now();

try {
final oldImageStream = _imageStream;
_imageStream = imageProvider.resolve(ImageConfiguration.empty);
try {
final oldImageStream = _imageStream;
_imageStream = imageProvider.resolve(ImageConfiguration.empty);

if (_imageStream!.key != oldImageStream?.key) {
oldImageStream?.removeListener(_listener);
if (_imageStream!.key != oldImageStream?.key) {
oldImageStream?.removeListener(_listener);

_listener = ImageStreamListener(
_onImageLoadSuccess,
onError: _onImageLoadError,
);
_imageStream!.addListener(_listener);
_listener = ImageStreamListener(
_onImageLoadSuccess,
onError: _onImageLoadError,
);
_imageStream!.addListener(_listener);
}
} catch (e, s) {
// Make sure all exceptions are handled - #444 / #536
_onImageLoadError(e, s);
}
} catch (e, s) {
// Make sure all exceptions are handled - #444 / #536
_onImageLoadError(e, s);
}

if (delay <= Duration.zero) return startLoad();
_loadDelayTimer = Timer(delay, startLoad);
}

void _onImageLoadSuccess(ImageInfo imageInfo, bool synchronousCall) {
Expand Down Expand Up @@ -233,6 +240,7 @@ class TileImage extends ChangeNotifier {
}
}

_loadDelayTimer?.cancel();
cancelLoading.complete();

_readyToDisplay = false;
Expand Down
2 changes: 1 addition & 1 deletion lib/src/layer/tile_layer/tile_image_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class TileImageManager {
tileBounds.atZoom(tile.coordinates.z).wrap(tile.coordinates),
layer,
);
tile.load();
tile.load(delay: layer.tileLoadDelay);
}
}

Expand Down
21 changes: 20 additions & 1 deletion lib/src/layer/tile_layer/tile_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,24 @@ class TileLayer extends StatefulWidget {
/// the [key].
final TileUpdateTransformer tileUpdateTransformer;

/// Duration between a tile becoming 'alive' (built/viewed) and it beginning
/// to load its resource image.
///
/// This allows tiles which are visible only very briefly (for example, during
/// fast gestures) to be destroyed (unviewed) without wasting resources (such
/// as network usage and metered tile loads) trying to load its image. This
/// may speed up loading of other images.
///
/// Note that destroyed tiles always stop loading if they are - but this
/// prevents even the start of their loading.
///
/// To change how map events and gestures influence how tiles load, such as
/// to delay all tile loading until a gesture has paused, see
/// [tileUpdateTransformer].
///
/// Defaults to 30ms. Set to `Duration.zero` to disable delay.
final Duration tileLoadDelay;

/// Create a new [TileLayer] for the [FlutterMap] widget.
TileLayer({
super.key,
Expand Down Expand Up @@ -261,6 +279,7 @@ class TileLayer extends StatefulWidget {
this.reset,
this.tileBounds,
TileUpdateTransformer? tileUpdateTransformer,
this.tileLoadDelay = const Duration(milliseconds: 30),
String userAgentPackageName = 'unknown',
}) : assert(
tileDisplay.when(
Expand Down Expand Up @@ -712,7 +731,7 @@ class _TileLayerState extends State<TileLayer> with TickerProviderStateMixin {

// Create the new Tiles.
for (final tile in tilesToLoad) {
tile.load();
tile.load(delay: widget.tileLoadDelay);
}
}

Expand Down
6 changes: 3 additions & 3 deletions lib/src/layer/tile_layer/tile_update_transformer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ abstract class TileUpdateTransformers {
/// This may improve performance, and reduce the number of tile requests, but
/// at the expense of UX: new tiles will not be loaded until [duration] after
/// the final tile load event in a series. For example, a fling gesture will
/// not load new tiles during its animation, only at the end. Best used in
/// combination with the cancellable tile provider, for even more fine-tuned
/// optimization.
/// not load new tiles during its animation, only at the end.
///
/// A more balanced tradeoff is the setting of [TileLayer.tileLoadDelay].
///
/// Implementation follows that in
/// ['package:stream_transform'](https://pub.dev/documentation/stream_transform/latest/stream_transform/RateLimit/debounce.html).
Expand Down
5 changes: 5 additions & 0 deletions test/flutter_map_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ void main() {
expect(find.byType(RawImage), findsWidgets);
expect(find.byType(MarkerLayer), findsWidgets);
expect(find.byType(FlutterLogo), findsOneWidget);
await tester.pumpAndSettle(
const Duration(milliseconds: 30),
EnginePhase.build,
const Duration(minutes: 1),
);
});

testWidgets(
Expand Down
6 changes: 6 additions & 0 deletions test/layer/circle_layer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,11 @@ void main() {
find.descendant(
of: find.byType(CircleLayer), matching: find.byType(CustomPaint)),
findsOneWidget);

await tester.pumpAndSettle(
const Duration(milliseconds: 30),
EnginePhase.build,
const Duration(minutes: 1),
);
});
}
5 changes: 5 additions & 0 deletions test/layer/marker_layer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,10 @@ void main() {
expect(find.byType(FlutterMap), findsOneWidget);
expect(find.byType(MarkerLayer), findsWidgets);
expect(find.byKey(key), findsOneWidget);
await tester.pumpAndSettle(
const Duration(milliseconds: 30),
EnginePhase.build,
const Duration(minutes: 1),
);
});
}
6 changes: 6 additions & 0 deletions test/layer/polygon_layer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ void main() {
find.descendant(
of: find.byType(PolygonLayer), matching: find.byType(CustomPaint)),
findsOneWidget);

await tester.pumpAndSettle(
const Duration(milliseconds: 30),
EnginePhase.build,
const Duration(minutes: 1),
);
});

test('polygon normal/rotation', () {
Expand Down
6 changes: 6 additions & 0 deletions test/layer/polyline_layer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,11 @@ void main() {
find.descendant(
of: find.byType(PolylineLayer), matching: find.byType(CustomPaint)),
findsOneWidget);

await tester.pumpAndSettle(
const Duration(milliseconds: 30),
EnginePhase.build,
const Duration(minutes: 1),
);
});
}
24 changes: 24 additions & 0 deletions test/map/map_controller_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ void main() {
expect(camera.center, equals(expectedCenter));
expect(camera.zoom, equals(expectedZoom));
}

await tester.pumpAndSettle(
const Duration(milliseconds: 30),
EnginePhase.build,
const Duration(minutes: 1),
);
});

testWidgets('test fit bounds methods with rotation', (tester) async {
Expand Down Expand Up @@ -620,6 +626,12 @@ void main() {
expectedCenter: const LatLng(1.2239447514276816, 31.954672909718134),
expectedZoom: 5.368867444131886,
);

await tester.pumpAndSettle(
const Duration(milliseconds: 30),
EnginePhase.build,
const Duration(minutes: 1),
);
});

testWidgets('test fit coordinates methods', (tester) async {
Expand Down Expand Up @@ -818,6 +830,12 @@ void main() {
expectedCenter: const LatLng(1.223944751427707, 31.954672909718177),
expectedZoom: 5.368867444131889,
);

await tester.pumpAndSettle(
const Duration(milliseconds: 30),
EnginePhase.build,
const Duration(minutes: 1),
);
});

testWidgets('test fit inside bounds with rotation', (tester) async {
Expand Down Expand Up @@ -1373,5 +1391,11 @@ void main() {
expectedCenter: const LatLng(1.2682880092901039, 31.90701622809375),
expectedZoom: 5.728195363812886,
);

await tester.pumpAndSettle(
const Duration(milliseconds: 30),
EnginePhase.build,
const Duration(minutes: 1),
);
});
}
Loading