From 98e7e499d4f09231a41ae314fcbe0c6cbf09735c Mon Sep 17 00:00:00 2001 From: stopflock Date: Sun, 7 Dec 2025 17:51:23 -0600 Subject: [PATCH] Pin max nodes indicator to screen, not map. Clean up cruft. --- V1.8.3_NODE_LIMIT_INDICATOR_FIX.md | 69 ++++++++++++++++++++++++++++++ assets/changelog.json | 6 +++ lib/widgets/map_view.dart | 45 ++++++++++--------- pubspec.yaml | 2 +- 4 files changed, 100 insertions(+), 22 deletions(-) create mode 100644 V1.8.3_NODE_LIMIT_INDICATOR_FIX.md diff --git a/V1.8.3_NODE_LIMIT_INDICATOR_FIX.md b/V1.8.3_NODE_LIMIT_INDICATOR_FIX.md new file mode 100644 index 0000000..8e35804 --- /dev/null +++ b/V1.8.3_NODE_LIMIT_INDICATOR_FIX.md @@ -0,0 +1,69 @@ +# V1.8.3 Node Limit Indicator Fix + +## Problem +The node limit indicator would disappear when the navigation sheet opened during search/routing, particularly noticeable on Android. The indicator would appear correctly when just the search bar showed, but disappear when the navigation sheet auto-opened. + +## Root Cause +The issue was in the **map positioning architecture**, specifically with `SheetAwareMap`. Here's what happens: + +1. **Search activated**: Search bar appears → node limit indicator shifts down 60px (works correctly) +2. **Navigation sheet opens**: Navigation sheet auto-opens → `sheetHeight` changes from 0 to ~300px +3. **Map repositioning**: `SheetAwareMap` uses `AnimatedPositioned` with `top: -sheetHeight` to move the entire map up +4. **Indicator disappears**: The node limit indicator, positioned at `top: 8.0 + searchBarOffset`, gets moved up by 300px along with the map, placing it off-screen + +The indicators were positioned relative to the map's coordinate system, but when the sheet opened, the entire map (including indicators) was moved up by the sheet height to keep the center visible above the sheet. + +## Solution +**Brutalist fix**: Move the node limit indicator out of the map coordinate system and into screen coordinates alongside other UI overlays. + +### Files Changed +- **map_view.dart**: Moved node limit indicator from inside SheetAwareMap to main Stack +- **pubspec.yaml**: Version bump to 1.8.3+33 +- **changelog.json**: Added release notes + +### Architecture Changes +```dart +// BEFORE - mixed coordinate systems (confusing!) +return Stack([ + SheetAwareMap( // Map coordinates + child: FlutterMap([ + cameraLayers: Stack([ + NodeLimitIndicator(...) // ❌ Map coordinates (moves with map) + ]) + ]) + ), + NetworkStatusIndicator(...), // ✅ Screen coordinates (fixed to screen) +]); + +// AFTER - consistent coordinate system (clean!) +return Stack([ + SheetAwareMap( // Map coordinates + child: FlutterMap([ + cameraLayers: Stack([ + // Only map data (nodes, overlays) - no UI indicators + ]) + ]) + ), + NodeLimitIndicator(...), // ✅ Screen coordinates (fixed to screen) + NetworkStatusIndicator(...), // ✅ Screen coordinates (fixed to screen) +]); +``` + +## Architecture Insight +The fix revealed a **mixed coordinate system anti-pattern**. All UI overlays (compass, search box, zoom buttons, indicators) should use screen coordinates for consistency. Only map data (nodes, overlays, FOV cones) should be in map coordinates. + +## Result +- Node limit indicator stays visible when navigation sheets open +- Network status indicator also fixed for consistency +- Indicators maintain correct screen position during all sheet transitions +- Consistent behavior across iOS and Android + +## Testing Notes +To test this fix: +1. Start app and wait for nodes to load (node limit indicator should appear if >max nodes) +2. Tap search button → search bar appears, indicator shifts down 60px +3. Navigation sheet auto-opens → indicator stays visible in screen position (no longer affected by map movement) +4. Cancel search → indicator returns to original position +5. Repeat workflow → should work reliably every time + +The fix ensures indicators stay in their intended screen positions using consistent coordinate system architecture. \ No newline at end of file diff --git a/assets/changelog.json b/assets/changelog.json index ce6926d..9860ece 100644 --- a/assets/changelog.json +++ b/assets/changelog.json @@ -1,4 +1,10 @@ { + "1.8.3": { + "content": [ + "• Fixed node limit indicator disappearing when navigation sheet opens during search/routing", + "• Improved indicator architecture - moved node limit indicator to screen coordinates for consistency with other UI overlays" + ] + }, "1.8.2": { "content": [ "• Fixed map positioning for node tags and suspected location sheets - map now correctly centers above sheet when opened", diff --git a/lib/widgets/map_view.dart b/lib/widgets/map_view.dart index 7926e84..7e495fe 100644 --- a/lib/widgets/map_view.dart +++ b/lib/widgets/map_view.dart @@ -4,7 +4,7 @@ import 'package:flutter_map_animations/flutter_map_animations.dart'; import 'package:latlong2/latlong.dart'; import 'package:provider/provider.dart'; -import '../app_state.dart'; +import '../app_state.dart' show AppState, FollowMeMode, UploadMode; import '../services/offline_area_service.dart'; import '../services/network_status.dart'; import '../services/prefetch_area_service.dart'; @@ -28,7 +28,6 @@ import 'network_status_indicator.dart'; import 'node_limit_indicator.dart'; import 'proximity_alert_banner.dart'; import '../dev_config.dart'; -import '../app_state.dart' show FollowMeMode; import '../services/proximity_alert_service.dart'; import 'sheet_aware_map.dart'; @@ -249,6 +248,11 @@ class MapViewState extends State { ); } + /// Calculate search bar offset for screen-positioned indicators + double _calculateScreenIndicatorSearchOffset(AppState appState) { + return (!appState.offlineMode && appState.isInSearchMode) ? 60.0 : 0.0; + } + @override void didUpdateWidget(covariant MapView oldWidget) { @@ -370,23 +374,6 @@ class MapViewState extends State { children: [ ...overlayLayers, markerLayer, - - // Node limit indicator (top-left) - shown when limit is active - Builder( - builder: (context) { - final appState = context.read(); - // Add search bar offset when search bar is visible - final searchBarOffset = (!appState.offlineMode && appState.isInSearchMode) ? 60.0 : 0.0; - - return NodeLimitIndicator( - isActive: nodeData.isLimitActive, - renderedCount: nodeData.nodesToRender.length, - totalCount: nodeData.validNodesCount, - top: 8.0 + searchBarOffset, - left: 8.0, - ); - }, - ), ], ); }, @@ -540,12 +527,28 @@ class MapViewState extends State { onSearchPressed: widget.onSearchPressed, ), + // Node limit indicator (top-left) - shown when limit is active + Builder( + builder: (context) { + final appState = context.watch(); + final searchBarOffset = _calculateScreenIndicatorSearchOffset(appState); + + return NodeLimitIndicator( + isActive: nodeData.isLimitActive, + renderedCount: nodeData.nodesToRender.length, + totalCount: nodeData.validNodesCount, + top: 8.0 + searchBarOffset, + left: 8.0, + ); + }, + ), + // Network status indicator (top-left) - conditionally shown if (appState.networkStatusIndicatorEnabled) Builder( builder: (context) { - // Calculate position based on node limit indicator presence and search bar - final searchBarOffset = (!appState.offlineMode && appState.isInSearchMode) ? 60.0 : 0.0; + final appState = context.watch(); + final searchBarOffset = _calculateScreenIndicatorSearchOffset(appState); final nodeLimitOffset = nodeData.isLimitActive ? 48.0 : 0.0; // Height of node limit indicator + spacing return NetworkStatusIndicator( diff --git a/pubspec.yaml b/pubspec.yaml index cc33a58..659fb8a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: deflockapp description: Map public surveillance infrastructure with OpenStreetMap publish_to: "none" -version: 1.8.2+32 # The thing after the + is the version code, incremented with each release +version: 1.8.3+33 # The thing after the + is the version code, incremented with each release environment: sdk: ">=3.5.0 <4.0.0" # oauth2_client 4.x needs Dart 3.5+