Problem
The exponential backoff for tile retries (2s → 4s → 8s → ... → 60s cap) works well for transient network errors, but when the device is fully offline (airplane mode, no wifi/cellular), retrying is pointless. It burns through the backoff progression and spams logs with DNS failures like:
ClientException with SocketException: Failed host lookup: 'tile.openstreetmap.org'
[TileLayerManager] Tile error at 14/2620/6333, scheduling retry in 2s
[TileLayerManager] Tile error at 14/2620/6333, scheduling retry in 4s
[TileLayerManager] Tile error at 14/2620/6333, scheduling retry in 8s
...
Proposed solution
Add connectivity_plus package to detect when the OS reports no network interfaces, and wire it into TileLayerManager:
- When offline (no wifi, no cellular): cancel the retry timer and stop scheduling new retries
- When connectivity restores: fire the reset stream immediately, reset backoff to 2s → tiles retry instantly
Limitation
connectivity_plus detects network interface availability (wifi/cellular up), not actual internet reachability. If the interface is up but DNS fails (e.g., captive portal, USB-only), the exponential backoff still handles it. This is the right trade-off — catches the common case (airplane mode, wifi off) without heavyweight reachability probes.
Implementation notes
Dependencies
- Add
connectivity_plus: ^6.1.0 to pubspec.yaml
- Add
ACCESS_NETWORK_STATE permission to android/app/src/main/AndroidManifest.xml (currently only has INTERNET)
Key changes to lib/widgets/map/tile_layer_manager.dart
// New fields
StreamSubscription<List<ConnectivityResult>>? _connectivitySub;
bool _isConnected = true;
// In initialize():
_connectivitySub = Connectivity().onConnectivityChanged.listen((results) {
final connected = results.any((r) => r != ConnectivityResult.none);
if (connected && !_isConnected) {
_isConnected = true;
_retryDelay = _minRetryDelay;
_resetController.add(null); // Retry immediately
} else if (!connected && _isConnected) {
_isConnected = false;
_retryTimer?.cancel(); // Stop retrying
}
});
// In scheduleRetry():
if (!_isConnected) return; // Skip when offline
// In dispose():
_connectivitySub?.cancel();
Current state of retry system
TileLayerManager already has exponential backoff: 2s → 4s → 8s → 16s → 32s → 60s cap
onTileLoadSuccess() resets backoff to 2s when a tile loads
scheduleRetry() is @visibleForTesting — connectivity tests can call it directly
- Existing test suite: 9 backoff tests in
test/widgets/map/tile_layer_manager_test.dart
Existing infrastructure
NetworkStatus service (lib/services/network_status.dart) tracks request-level status (loading/success/error) — NOT device connectivity. Don't conflate the two.
AppState.offlineMode is a user-controlled manual toggle — separate concern
- No existing connectivity detection package in the project
Tests to add
scheduleRetry is a no-op when offline
- Connectivity restored fires reset stream and resets backoff
- Connectivity lost cancels pending retry timer
Expected behavior after fix
| Scenario |
Current |
After |
| Airplane mode on |
Retries every 2s→60s forever |
Stops retrying, waits for connectivity |
| Airplane mode off |
Waits up to 60s for next retry |
Retries immediately, backoff reset to 2s |
| Spotty wifi |
Exponential backoff |
Same (connectivity_plus still reports "connected") |
| Captive portal |
Exponential backoff |
Same |
Problem
The exponential backoff for tile retries (2s → 4s → 8s → ... → 60s cap) works well for transient network errors, but when the device is fully offline (airplane mode, no wifi/cellular), retrying is pointless. It burns through the backoff progression and spams logs with DNS failures like:
Proposed solution
Add
connectivity_pluspackage to detect when the OS reports no network interfaces, and wire it intoTileLayerManager:Limitation
connectivity_plusdetects network interface availability (wifi/cellular up), not actual internet reachability. If the interface is up but DNS fails (e.g., captive portal, USB-only), the exponential backoff still handles it. This is the right trade-off — catches the common case (airplane mode, wifi off) without heavyweight reachability probes.Implementation notes
Dependencies
connectivity_plus: ^6.1.0topubspec.yamlACCESS_NETWORK_STATEpermission toandroid/app/src/main/AndroidManifest.xml(currently only hasINTERNET)Key changes to
lib/widgets/map/tile_layer_manager.dartCurrent state of retry system
TileLayerManageralready has exponential backoff: 2s → 4s → 8s → 16s → 32s → 60s caponTileLoadSuccess()resets backoff to 2s when a tile loadsscheduleRetry()is@visibleForTesting— connectivity tests can call it directlytest/widgets/map/tile_layer_manager_test.dartExisting infrastructure
NetworkStatusservice (lib/services/network_status.dart) tracks request-level status (loading/success/error) — NOT device connectivity. Don't conflate the two.AppState.offlineModeis a user-controlled manual toggle — separate concernTests to add
scheduleRetryis a no-op when offlineExpected behavior after fix