Better pins, bugfixes

This commit is contained in:
stopflock
2025-10-02 18:29:17 -05:00
parent bac033528c
commit 19de232484
7 changed files with 391 additions and 421 deletions
+5 -5
View File
@@ -120,16 +120,16 @@ class MapOverlays extends StatelessWidget {
builder: (context, appState, child) {
return Column(
children: [
// Search/Route button (top of controls) - hide when in search/route modes
if (onSearchPressed != null && !appState.isInSearchMode && !appState.isInRouteMode)
// Navigation button - simplified logic
if (onSearchPressed != null && (appState.showSearchButton || appState.showRouteButton))
FloatingActionButton(
mini: true,
heroTag: "search_nav",
onPressed: onSearchPressed,
tooltip: appState.hasActiveRoute ? 'Route Overview' : 'Search Location',
child: Icon(appState.hasActiveRoute ? Icons.route : Icons.search),
tooltip: appState.showRouteButton ? 'Route Overview' : 'Search Location',
child: Icon(appState.showRouteButton ? Icons.route : Icons.search),
),
if (onSearchPressed != null && !appState.isInSearchMode && !appState.isInRouteMode)
if (onSearchPressed != null && (appState.showSearchButton || appState.showRouteButton))
const SizedBox(height: 8),
// Layer selector button
+28 -3
View File
@@ -382,16 +382,41 @@ class MapViewState extends State<MapView> {
centerMarkers.add(
Marker(
point: appState.provisionalPinLocation!,
width: 48.0,
height: 48.0,
width: 32.0,
height: 32.0,
child: const ProvisionalPin(),
),
);
}
// Build start/end pins for route visualization
if (appState.showingOverview || appState.isInRouteMode) {
if (appState.routeStart != null) {
centerMarkers.add(
Marker(
point: appState.routeStart!,
width: 32.0,
height: 32.0,
child: const LocationPin(type: PinType.start),
),
);
}
if (appState.routeEnd != null) {
centerMarkers.add(
Marker(
point: appState.routeEnd!,
width: 32.0,
height: 32.0,
child: const LocationPin(type: PinType.end),
),
);
}
}
// Build route path visualization
final routeLines = <Polyline>[];
if (appState.routePath != null && appState.routePath!.length > 1) {
if (appState.routePath != null && appState.routePath!.length > 1 &&
(appState.showingOverview || appState.isInRouteMode)) {
routeLines.add(Polyline(
points: appState.routePath!,
color: Colors.blue,
+160 -228
View File
@@ -50,6 +50,20 @@ class NavigationSheet extends StatelessWidget {
);
}
Widget _buildDragHandle() {
return Center(
child: Container(
width: 40,
height: 4,
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(2),
),
),
);
}
@override
Widget build(BuildContext context) {
return Consumer<AppState>(
@@ -58,258 +72,176 @@ class NavigationSheet extends StatelessWidget {
final provisionalLocation = appState.provisionalPinLocation;
final provisionalAddress = appState.provisionalPinAddress;
if (provisionalLocation == null) {
if (provisionalLocation == null && !appState.showingOverview) {
return const SizedBox.shrink();
}
switch (navigationMode) {
case AppNavigationMode.search:
return Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Drag handle
Center(
child: Container(
width: 40,
height: 4,
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(2),
return Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildDragHandle(),
// SEARCH MODE: Initial location with route options
if (navigationMode == AppNavigationMode.search && !appState.isSettingSecondPoint && !appState.isCalculating && !appState.showingOverview && provisionalLocation != null) ...[
_buildLocationInfo(
label: 'Location',
coordinates: provisionalLocation,
address: provisionalAddress,
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
icon: const Icon(Icons.directions),
label: const Text('Route To'),
onPressed: () {
appState.startRoutePlanning(thisLocationIsStart: false);
},
),
),
),
// Location info
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
icon: const Icon(Icons.my_location),
label: const Text('Route From'),
onPressed: () {
appState.startRoutePlanning(thisLocationIsStart: true);
},
),
),
],
),
],
// SETTING SECOND POINT: Show both points and select button
if (appState.isSettingSecondPoint && provisionalLocation != null) ...[
// Show existing route points
if (appState.routeStart != null) ...[
_buildLocationInfo(
label: 'Location',
coordinates: provisionalLocation,
address: provisionalAddress,
label: 'Start',
coordinates: appState.routeStart!,
address: appState.routeStartAddress,
),
const SizedBox(height: 12),
],
if (appState.routeEnd != null) ...[
_buildLocationInfo(
label: 'End',
coordinates: appState.routeEnd!,
address: appState.routeEndAddress,
),
const SizedBox(height: 12),
],
// Show the point we're selecting
_buildLocationInfo(
label: appState.settingRouteStart ? 'Start (select)' : 'End (select)',
coordinates: provisionalLocation,
address: provisionalAddress,
),
const SizedBox(height: 16),
ElevatedButton.icon(
icon: const Icon(Icons.check),
label: const Text('Select Location'),
onPressed: () {
debugPrint('[NavigationSheet] Select Location button pressed');
appState.selectSecondRoutePoint();
},
),
],
// CALCULATING: Show loading
if (appState.isCalculating) ...[
const Center(
child: SizedBox(
width: 40,
height: 40,
child: CircularProgressIndicator(),
),
),
const SizedBox(height: 16),
const Text('Calculating route...', textAlign: TextAlign.center),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => appState.cancelNavigation(),
child: const Text('Cancel'),
),
],
// ROUTE OVERVIEW: Show route details with start/cancel options
if (appState.showingOverview) ...[
if (appState.routeStart != null) ...[
_buildLocationInfo(
label: 'Start',
coordinates: appState.routeStart!,
address: appState.routeStartAddress,
),
const SizedBox(height: 12),
],
if (appState.routeEnd != null) ...[
_buildLocationInfo(
label: 'End',
coordinates: appState.routeEnd!,
address: appState.routeEndAddress,
),
const SizedBox(height: 12),
],
if (appState.routeDistance != null) ...[
Text(
'Distance: ${(appState.routeDistance! / 1000).toStringAsFixed(1)} km',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
// Action buttons
Row(
children: [
],
Row(
children: [
if (navigationMode == AppNavigationMode.search) ...[
// Route preview mode - start or cancel
Expanded(
child: ElevatedButton.icon(
icon: const Icon(Icons.directions),
label: const Text('Route To'),
onPressed: () {
appState.startRouteSetup(settingStart: false);
},
icon: const Icon(Icons.play_arrow),
label: const Text('Start'),
onPressed: () => appState.startRoute(),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
icon: const Icon(Icons.my_location),
label: const Text('Route From'),
onPressed: () {
appState.startRouteSetup(settingStart: true);
},
icon: const Icon(Icons.close),
label: const Text('Cancel'),
onPressed: () => appState.cancelNavigation(),
),
),
],
),
],
),
);
case AppNavigationMode.routeSetup:
return Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Drag handle
Center(
child: Container(
width: 40,
height: 4,
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(2),
),
),
),
// Route points info
if (appState.routeStart != null) ...[
_buildLocationInfo(
label: 'Start',
coordinates: appState.routeStart!,
address: appState.routeStartAddress,
),
const SizedBox(height: 12),
],
if (appState.routeEnd != null) ...[
_buildLocationInfo(
label: 'End',
coordinates: appState.routeEnd!,
address: appState.routeEndAddress,
),
const SizedBox(height: 12),
],
_buildLocationInfo(
label: appState.settingRouteStart ? 'Start (select)' : 'End (select)',
coordinates: provisionalLocation,
address: provisionalAddress,
),
const SizedBox(height: 16),
// Select location button
ElevatedButton.icon(
icon: const Icon(Icons.check),
label: const Text('Select Location'),
onPressed: () {
debugPrint('[NavigationSheet] Select Location button pressed');
appState.selectRouteLocation();
},
),
],
),
);
case AppNavigationMode.routeCalculating:
return Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Drag handle
Center(
child: Container(
width: 40,
height: 4,
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(2),
),
),
),
const CircularProgressIndicator(),
const SizedBox(height: 16),
const Text('Calculating route...'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
appState.cancelRoute();
},
child: const Text('Cancel'),
),
],
),
);
case AppNavigationMode.routePreview:
case AppNavigationMode.routeOverview:
return Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Drag handle
Center(
child: Container(
width: 40,
height: 4,
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(2),
),
),
),
// Route info
if (appState.routeStart != null) ...[
_buildLocationInfo(
label: 'Start',
coordinates: appState.routeStart!,
address: appState.routeStartAddress,
),
const SizedBox(height: 12),
],
if (appState.routeEnd != null) ...[
_buildLocationInfo(
label: 'End',
coordinates: appState.routeEnd!,
address: appState.routeEndAddress,
),
const SizedBox(height: 12),
],
// Distance info
if (appState.routeDistance != null) ...[
Text(
'Distance: ${(appState.routeDistance! / 1000).toStringAsFixed(1)} km',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
],
// Action buttons
Row(
children: [
if (navigationMode == AppNavigationMode.routePreview) ...[
Expanded(
child: ElevatedButton.icon(
icon: const Icon(Icons.play_arrow),
label: const Text('Start'),
onPressed: () {
appState.startRoute();
},
),
] else if (navigationMode == AppNavigationMode.routeActive) ...[
// Active route overview - resume or cancel
Expanded(
child: ElevatedButton.icon(
icon: const Icon(Icons.play_arrow),
label: const Text('Resume'),
onPressed: () => appState.hideRouteOverview(),
),
const SizedBox(width: 12),
] else if (navigationMode == AppNavigationMode.routeOverview) ...[
Expanded(
child: ElevatedButton.icon(
icon: const Icon(Icons.play_arrow),
label: const Text('Resume'),
onPressed: () {
appState.returnToActiveRoute();
},
),
),
const SizedBox(width: 12),
],
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
icon: const Icon(Icons.close),
label: const Text('Cancel'),
onPressed: () {
appState.cancelRoute();
},
label: const Text('End Route'),
onPressed: () => appState.cancelRoute(),
),
),
],
),
],
),
);
default:
return const SizedBox.shrink();
}
],
),
],
],
),
);
},
);
}
+55 -21
View File
@@ -1,19 +1,36 @@
import 'package:flutter/material.dart';
/// A pin icon for marking provisional locations during search/routing
class ProvisionalPin extends StatelessWidget {
enum PinType {
provisional, // Orange - current selection
start, // Green - route start
end, // Red - route end
}
/// A thumbtack-style pin for marking locations during search/routing
class LocationPin extends StatelessWidget {
final PinType type;
final double size;
final Color color;
const ProvisionalPin({
const LocationPin({
super.key,
this.size = 48.0,
this.color = Colors.red,
required this.type,
this.size = 32.0, // Smaller than before
});
Color get _pinColor {
switch (type) {
case PinType.provisional:
return Colors.orange;
case PinType.start:
return Colors.green;
case PinType.end:
return Colors.red;
}
}
@override
Widget build(BuildContext context) {
return Container(
return SizedBox(
width: size,
height: size,
child: Stack(
@@ -21,34 +38,34 @@ class ProvisionalPin extends StatelessWidget {
children: [
// Pin shadow
Positioned(
bottom: 0,
bottom: 2,
child: Container(
width: size * 0.3,
height: size * 0.15,
width: size * 0.4,
height: size * 0.2,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3),
borderRadius: BorderRadius.circular(size * 0.15),
borderRadius: BorderRadius.circular(size * 0.1),
),
),
),
// Main pin
// Main thumbtack pin
Icon(
Icons.location_pin,
Icons.push_pin,
size: size,
color: color,
color: _pinColor,
),
// Inner dot
// Inner dot for better visibility
Positioned(
top: size * 0.15,
top: size * 0.2,
child: Container(
width: size * 0.25,
height: size * 0.25,
width: size * 0.3,
height: size * 0.3,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
border: Border.all(
color: color.withOpacity(0.8),
width: 2,
color: _pinColor.withOpacity(0.8),
width: 1.5,
),
),
),
@@ -57,4 +74,21 @@ class ProvisionalPin extends StatelessWidget {
),
);
}
}
// Legacy widget name for compatibility
class ProvisionalPin extends StatelessWidget {
final double size;
final Color color;
const ProvisionalPin({
super.key,
this.size = 32.0,
this.color = Colors.orange,
});
@override
Widget build(BuildContext context) {
return LocationPin(type: PinType.provisional, size: size);
}
}