diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index ce3d77a..0854610 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -356,6 +356,7 @@ class _HomeScreenState extends State with TickerProviderStateMixin { sheetHeight: activeSheetHeight, selectedNodeId: _selectedNodeId, onNodeTap: openNodeTagSheet, + onSearchPressed: _onNavigationButtonPressed, onUserGesture: () { if (appState.followMeMode != FollowMeMode.off) { appState.setFollowMeMode(FollowMeMode.off); @@ -438,16 +439,6 @@ class _HomeScreenState extends State with TickerProviderStateMixin { ), ), ), - // Search button as floating map control - Positioned( - bottom: 200, // Position above other controls - right: 16, - child: FloatingActionButton( - onPressed: _onNavigationButtonPressed, - tooltip: appState.hasActiveRoute ? 'Route Overview' : 'Search Location', - child: Icon(appState.hasActiveRoute ? Icons.route : Icons.search), - ), - ), ], ), ), diff --git a/lib/state/navigation_state.dart b/lib/state/navigation_state.dart index 18a58c9..41cdbea 100644 --- a/lib/state/navigation_state.dart +++ b/lib/state/navigation_state.dart @@ -109,7 +109,17 @@ class NavigationState extends ChangeNotifier { _provisionalPinLocation = null; _provisionalPinAddress = null; _clearSearchResults(); - debugPrint('[NavigationState] Cancelled search mode'); + + // Also clear any partial route data + if (_routeStart != null && _routeEnd == null) { + _routeStart = null; + _routeStartAddress = null; + } else if (_routeEnd != null && _routeStart == null) { + _routeEnd = null; + _routeEndAddress = null; + } + + debugPrint('[NavigationState] Cancelled search mode - cleaned up provisional pin'); notifyListeners(); } @@ -137,15 +147,32 @@ class NavigationState extends ChangeNotifier { /// Start route setup (user clicked "route to" or "route from") void startRouteSetup({required bool settingStart}) { - if (_mode != AppNavigationMode.search || _provisionalPinLocation == null) return; + debugPrint('[NavigationState] startRouteSetup called - settingStart: $settingStart, mode: $_mode, location: $_provisionalPinLocation'); + + if (_mode != AppNavigationMode.search || _provisionalPinLocation == null) { + debugPrint('[NavigationState] startRouteSetup - early return'); + return; + } + + // Clear any previous route data + _routeStart = null; + _routeEnd = null; + _routeStartAddress = null; + _routeEndAddress = null; + _routePath = null; + _routeDistance = null; _settingRouteStart = settingStart; if (settingStart) { + // "Route From" - this location is the START, we need to pick END _routeStart = _provisionalPinLocation; _routeStartAddress = _provisionalPinAddress; + debugPrint('[NavigationState] Set route start: $_routeStart'); } else { + // "Route To" - this location is the END, we need to pick START _routeEnd = _provisionalPinLocation; _routeEndAddress = _provisionalPinAddress; + debugPrint('[NavigationState] Set route end: $_routeEnd'); } _mode = AppNavigationMode.routeSetup; @@ -156,16 +183,25 @@ class NavigationState extends ChangeNotifier { /// Lock in second route location void selectRouteLocation() { - if (_mode != AppNavigationMode.routeSetup || _provisionalPinLocation == null) return; + debugPrint('[NavigationState] selectRouteLocation called - mode: $_mode, provisional: $_provisionalPinLocation'); + + if (_mode != AppNavigationMode.routeSetup || _provisionalPinLocation == null) { + debugPrint('[NavigationState] selectRouteLocation - early return (mode: $_mode, location: $_provisionalPinLocation)'); + return; + } if (_settingRouteStart) { _routeStart = _provisionalPinLocation; _routeStartAddress = _provisionalPinAddress; + debugPrint('[NavigationState] Set route start: $_routeStart'); } else { _routeEnd = _provisionalPinLocation; _routeEndAddress = _provisionalPinAddress; + debugPrint('[NavigationState] Set route end: $_routeEnd'); } + debugPrint('[NavigationState] Route points - start: $_routeStart, end: $_routeEnd'); + // Start route calculation _calculateRoute(); } diff --git a/lib/widgets/map/map_overlays.dart b/lib/widgets/map/map_overlays.dart index 53b39fa..c357116 100644 --- a/lib/widgets/map/map_overlays.dart +++ b/lib/widgets/map/map_overlays.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; +import 'package:provider/provider.dart'; import '../../app_state.dart'; import '../../dev_config.dart'; @@ -13,6 +14,7 @@ class MapOverlays extends StatelessWidget { final AddNodeSession? session; final EditNodeSession? editSession; final String? attribution; // Attribution for current tile provider + final VoidCallback? onSearchPressed; // Callback for search button const MapOverlays({ super.key, @@ -21,6 +23,7 @@ class MapOverlays extends StatelessWidget { this.session, this.editSession, this.attribution, + this.onSearchPressed, }); @override @@ -113,45 +116,58 @@ class MapOverlays extends StatelessWidget { Positioned( bottom: bottomPositionFromButtonBar(kZoomControlsSpacingAboveButtonBar, MediaQuery.of(context).padding.bottom), right: 16, - child: Column( - children: [ - // Layer selector button - const LayerSelectorButton(), - const SizedBox(height: 8), - // Zoom in button - FloatingActionButton( - mini: true, - heroTag: "zoom_in", - onPressed: () { - try { - final zoom = mapController.camera.zoom; - mapController.move(mapController.camera.center, zoom + 1); - } catch (_) { - // Map controller not ready yet - } - }, - child: const Icon(Icons.add), - ), - const SizedBox(height: 8), - // Zoom out button - FloatingActionButton( - mini: true, - heroTag: "zoom_out", - onPressed: () { - try { - final zoom = mapController.camera.zoom; - mapController.move(mapController.camera.center, zoom - 1); - } catch (_) { - // Map controller not ready yet - } - }, - child: const Icon(Icons.remove), - ), - ], + child: Consumer( + builder: (context, appState, child) { + return Column( + children: [ + // Search/Route button (top of controls) + if (onSearchPressed != null) + FloatingActionButton( + mini: true, + heroTag: "search_nav", + onPressed: onSearchPressed, + tooltip: appState.hasActiveRoute ? 'Route Overview' : 'Search Location', + child: Icon(appState.hasActiveRoute ? Icons.route : Icons.search), + ), + if (onSearchPressed != null) const SizedBox(height: 8), + + // Layer selector button + const LayerSelectorButton(), + const SizedBox(height: 8), + // Zoom in button + FloatingActionButton( + mini: true, + heroTag: "zoom_in", + onPressed: () { + try { + final zoom = mapController.camera.zoom; + mapController.move(mapController.camera.center, zoom + 1); + } catch (_) { + // Map controller not ready yet + } + }, + child: const Icon(Icons.add), + ), + const SizedBox(height: 8), + // Zoom out button + FloatingActionButton( + mini: true, + heroTag: "zoom_out", + onPressed: () { + try { + final zoom = mapController.camera.zoom; + mapController.move(mapController.camera.center, zoom - 1); + } catch (_) { + // Map controller not ready yet + } + }, + child: const Icon(Icons.remove), + ), + ], + ); + }, ), ), - - ], ); } diff --git a/lib/widgets/map_view.dart b/lib/widgets/map_view.dart index 10530fc..ef7f573 100644 --- a/lib/widgets/map_view.dart +++ b/lib/widgets/map_view.dart @@ -38,6 +38,7 @@ class MapView extends StatefulWidget { this.sheetHeight = 0.0, this.selectedNodeId, this.onNodeTap, + this.onSearchPressed, }); final FollowMeMode followMeMode; @@ -45,6 +46,7 @@ class MapView extends StatefulWidget { final double sheetHeight; final int? selectedNodeId; final void Function(OsmNode)? onNodeTap; + final VoidCallback? onSearchPressed; @override State createState() => MapViewState(); @@ -497,6 +499,7 @@ class MapViewState extends State { session: session, editSession: editSession, attribution: appState.selectedTileType?.attribution, + onSearchPressed: widget.onSearchPressed, ), // Network status indicator (top-left) - conditionally shown diff --git a/lib/widgets/search_bar.dart b/lib/widgets/search_bar.dart index 5bd7c45..e5c6941 100644 --- a/lib/widgets/search_bar.dart +++ b/lib/widgets/search_bar.dart @@ -180,22 +180,25 @@ class _LocationSearchBarState extends State { focusNode: _focusNode, decoration: InputDecoration( hintText: 'Search places or coordinates...', - prefixIcon: const Icon(Icons.search), - suffixIcon: Row( + prefixIcon: Row( mainAxisSize: MainAxisSize.min, children: [ - if (_controller.text.isNotEmpty) - IconButton( - icon: const Icon(Icons.clear), - onPressed: _onClear, - ), IconButton( icon: const Icon(Icons.close), onPressed: _onCancel, tooltip: 'Cancel search', ), + const Icon(Icons.search), ], ), + prefixIconConstraints: const BoxConstraints(minWidth: 80), + suffixIcon: _controller.text.isNotEmpty + ? IconButton( + icon: const Icon(Icons.clear), + onPressed: _onClear, + tooltip: 'Clear text', + ) + : null, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none,