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>
65 lines
2.2 KiB
Dart
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 {}
|
|
}
|