Repository navigation
Description
What is the bug?
Since 8.3.0, when tiles fail to load (for example without a connection), the map no longer shows the loaded tiles of other zoom levels in their place: it goes blank.
In 8.2.2, after zooming in while offline, the lower-zoom tiles already loaded stayed visible, scaled up. This is the fallback discussed in #1397. In 8.3.0 to 8.3.2 and on master, the same steps give an empty map.
The cause seems to be #2166. To let tileBuilder draw failed tiles, _onImageLoadError now always calls _display(), which marks the failed tile readyToDisplay even without an errorImage. TileImageView.renderTiles only keeps ancestors and children for tiles that are not ready, so the fallback tiles are dropped. That also contradicts the documentation of readyToDisplay: "Loading errored but an error image is configured."
Found on Android (emulator, API 35) with Flutter 3.47.6, in an app meant to keep working on a spotty connection in the terrain.
How can we reproduce it?
The tile provider below stands in for losing the connection: it serves OpenStreetMap tiles up to zoom 10 and fails above.
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
void main() => runApp(const MaterialApp(home: ReproPage()));
class ReproPage extends StatelessWidget {
const ReproPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: FlutterMap(
options: const MapOptions(
initialCenter: LatLng(51.5, -0.09),
initialZoom: 10,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'dev.fleaflet.flutter_map.repro',
tileProvider: OfflineAboveZoomTileProvider(maxZoom: 10),
),
],
),
);
}
}
/// Behaves like a device that lost its connection after loading tiles up to
/// [maxZoom]: tiles above it fail to load.
class OfflineAboveZoomTileProvider extends TileProvider {
OfflineAboveZoomTileProvider({required this.maxZoom});
final int maxZoom;
@override
ImageProvider getImage(TileCoordinates coordinates, TileLayer options) =>
NetworkImage(
coordinates.z <= maxZoom
? getTileUrl(coordinates, options)
: 'https://offline.invalid/${coordinates.z}/${coordinates.x}/${coordinates.y}.png',
headers: headers,
);
}- Run it and wait for the tiles to load.
- Double-tap twice to zoom in to zoom 12.
Expected (8.2.2): the zoom 10 tiles stay visible, scaled up.
Actual (8.3.0 and later): the map is blank.
Do you have a potential solution?
Keep a failed tile without errorImage not ready to display, but still notify its listeners, so that Tile rebuilds and tileBuilder gets the failed tile:
if (errorImage != null) {
_display();
} else {
notifyListeners();
}The tileBuilder from #2166's example then still draws its text for failed tiles, on top of the fallback tiles, since tiles at the current zoom are drawn last. I have this with a test and can open a PR.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity