Files
PilotVault/Fly App/lib/ui/osm_tiles.dart
T
tajniak81andClaude Opus 5.5 91520560a7 Retry map tiles that failed while they are still on screen
The previous fix silenced tile errors, which made flutter_map count a failed
tile as a loaded transparent one, so it was never fetched again. Errors now
reach the layer, and OsmTileLayer rebuilds itself a few seconds after a
failure: loaded tiles come back from the image cache and only the failed ones
are requested again. Seen on the tablet, whose 2.4 GHz Wi-Fi loses a fifth of
its packets next to the OcuSync remote.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:35:19 +02:00

65 lines
2.2 KiB
Dart

import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:http/http.dart' show Client;
import 'package:http/retry.dart';
/// The OpenStreetMap base layer for every map in the app.
///
/// flutter_map never fetches a failed tile again while it stays on screen,
/// and on a bad link (the tablet's 2.4 GHz Wi-Fi next to an OcuSync remote)
/// that left dark squares on the map. So the client also retries dropped
/// connections, and a failure rebuilds the layer a little later: tiles that
/// loaded come back from the image cache, the failed ones are fetched again.
class OsmTileLayer extends StatefulWidget {
const OsmTileLayer({super.key, this.retryAfter = const Duration(seconds: 5)});
final Duration retryAfter;
@override
State<OsmTileLayer> createState() => _OsmTileLayerState();
}
class _OsmTileLayerState extends State<OsmTileLayer> {
int _generation = 0;
Timer? _retry;
void _onError(TileImage _, Object __, StackTrace? ___) {
_retry ??= Timer(widget.retryAfter, () {
_retry = null;
if (mounted) setState(() => _generation++);
});
}
@override
void dispose() {
_retry?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) => TileLayer(
key: ValueKey<int>(_generation),
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'com.pilotvault.flyapp',
tileProvider: _OsmTileProvider.instance,
evictErrorTileStrategy: EvictErrorTileStrategy.notVisibleRespectMargin,
errorTileCallback: _onError,
);
}
/// One provider for the app's lifetime, so rebuilds hand every map the same
/// instance (a new one makes the layer reload all its tiles). A provider
/// normally closes its client when its layer goes; this one is shared, so it
/// keeps the client open. Errors are not silenced: a silenced failure counts
/// as a loaded (transparent) tile and is never fetched again.
class _OsmTileProvider extends NetworkTileProvider {
_OsmTileProvider() : super(httpClient: RetryClient(Client(), whenError: (Object _, StackTrace __) => true));
static final _OsmTileProvider instance = _OsmTileProvider();
@override
Future<void> dispose() async {}
}