fixes tile re-rendering after long rate limit periods without having to zoom/pan

This commit is contained in:
stopflock
2025-08-22 23:44:49 -05:00
parent 01f73322c7
commit f6adffc84e
6 changed files with 221 additions and 0 deletions
+23
View File
@@ -63,6 +63,10 @@ class _MapViewState extends State<MapView> {
// Ensure initial overlays are fetched
WidgetsBinding.instance.addPostFrameCallback((_) {
// Set up tile refresh callback
final tileProvider = Provider.of<TileProviderWithCache>(context, listen: false);
tileProvider.setOnTilesCachedCallback(_onTilesCached);
_refreshCamerasFromProvider();
});
}
@@ -72,6 +76,15 @@ class _MapViewState extends State<MapView> {
_positionSub?.cancel();
_debounce.dispose();
_cameraProvider.removeListener(_onCamerasUpdated);
// Clean up tile refresh callback
try {
final tileProvider = Provider.of<TileProviderWithCache>(context, listen: false);
tileProvider.setOnTilesCachedCallback(null);
} catch (e) {
// Context might be disposed already - that's okay
}
super.dispose();
}
@@ -79,6 +92,14 @@ class _MapViewState extends State<MapView> {
if (mounted) setState(() {});
}
void _onTilesCached() {
// When new tiles are cached, just trigger a widget rebuild
// This should cause the TileLayer to re-render with cached tiles
if (mounted) {
setState(() {});
}
}
void _refreshCamerasFromProvider() {
final appState = context.read<AppState>();
LatLngBounds? bounds;
@@ -159,6 +180,8 @@ class _MapViewState extends State<MapView> {
@override
Widget build(BuildContext context) {
final appState = context.watch<AppState>();
+77
View File
@@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../services/network_status.dart';
class NetworkStatusIndicator extends StatelessWidget {
const NetworkStatusIndicator({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: NetworkStatus.instance,
child: Consumer<NetworkStatus>(
builder: (context, networkStatus, child) {
if (!networkStatus.hasAnyIssues) {
return const SizedBox.shrink();
}
String message;
IconData icon;
Color color;
switch (networkStatus.currentIssueType) {
case NetworkIssueType.osmTiles:
message = 'OSM tiles slow';
icon = Icons.map_outlined;
color = Colors.orange;
break;
case NetworkIssueType.overpassApi:
message = 'Camera data slow';
icon = Icons.camera_alt_outlined;
color = Colors.orange;
break;
case NetworkIssueType.both:
message = 'Network issues';
icon = Icons.cloud_off_outlined;
color = Colors.red;
break;
default:
return const SizedBox.shrink();
}
return Positioned(
top: MediaQuery.of(context).padding.top + 8,
left: 8,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color, width: 1),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon,
size: 16,
color: color,
),
const SizedBox(width: 4),
Text(
message,
style: TextStyle(
color: color,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
],
),
),
);
},
),
);
}
}
@@ -10,9 +10,15 @@ class TileProviderWithCache extends TileProvider with ChangeNotifier {
bool _disposed = false;
int _disposeCount = 0;
VoidCallback? _onTilesCachedCallback;
TileProviderWithCache();
/// Set a callback to be called when tiles are cached (used by MapView for refresh)
void setOnTilesCachedCallback(VoidCallback? callback) {
_onTilesCachedCallback = callback;
}
@override
void dispose() {
_disposeCount++;
@@ -67,6 +73,8 @@ class TileProviderWithCache extends TileProvider with ChangeNotifier {
if (!_disposed && hasListeners) {
notifyListeners(); // This updates any listening widgets
}
// Trigger map refresh callback to force tile re-rendering
_onTilesCachedCallback?.call();
}
// If bytes were empty, don't cache (will re-attempt next time)
} catch (e) {