feat: Enhance map functionality with download area selection and user location pointer

- Added download area selection feature in MapProvider with methods to enter, exit, and update bounds.
- Implemented DownloadAreaOverlay widget for user interface during download area selection.
- Integrated download area preview in MapManagementScreen with visual feedback.
- Introduced LocationPointer widget to indicate user location and heading on the map.
- Updated localization strings in app_sl.arb for improved clarity and accuracy.
- Refactored map marker generation to utilize the new LocationPointer for user location representation.
- Improved error handling in LocationTrackingService for initial position setting.
This commit is contained in:
Janez T
2025-10-28 19:09:11 +01:00
parent fed4aa19f1
commit da3302b030
14 changed files with 769 additions and 272 deletions

View File

@@ -412,9 +412,10 @@ class LocationTrackingService {
debugPrint('✅ [LocationTracking] Initial position set without broadcast');
debugPrint(' Next broadcast allowed in ${minTimeIntervalSeconds}s');
} catch (e) {
debugPrint(' [LocationTracking] Failed to set initial position: $e');
onError?.call('Failed to set initial position: $e');
debugPrint('⚠️ [LocationTracking] Failed to set initial position: $e');
debugPrint(' Will retry on next GPS update');
// Don't mark as set on failure, so it will retry on next update
// Don't call onError - this is not critical since it will retry automatically
}
}

View File

@@ -5,6 +5,7 @@ import 'package:latlong2/latlong.dart';
import 'package:geolocator/geolocator.dart';
import '../models/contact.dart';
import '../models/sar_marker.dart';
import '../widgets/map/location_pointer.dart';
/// Centralized service for map marker management.
///
@@ -222,47 +223,31 @@ class MapMarkerService {
}).toList();
}
/// Generate user location marker.
/// Generate user location marker with directional pointer.
///
/// Parameters:
/// - [position]: Current GPS position
/// - [heading]: Current heading in degrees (0-360, where 0 = North)
/// Pass null or -1 if heading unavailable
/// - [context]: Build context for theme access
///
/// Returns null if position is unavailable.
Marker? generateUserLocationMarker({
required Position? position,
double? heading,
required BuildContext context,
}) {
if (position == null) return null;
return Marker(
point: LatLng(position.latitude, position.longitude),
width: 40,
height: 40,
rotate: false, // Don't rotate with map
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.3),
shape: BoxShape.circle,
),
child: Container(
margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
shape: BoxShape.circle,
boxShadow: const [
BoxShadow(
color: Colors.black26,
blurRadius: 4,
),
],
),
child: const Icon(
Icons.my_location,
color: Colors.white,
size: 16,
),
),
width: 60,
height: 60,
rotate: false, // Don't rotate with map - we handle rotation internally
child: LocationPointer(
heading: heading,
color: Theme.of(context).colorScheme.primary,
size: 60,
),
);
}

View File

@@ -468,8 +468,9 @@ class MeshCoreBleService {
required double latitude,
required double longitude,
}) async {
// This command returns OK (0x00) response, so wait for acknowledgment
await _commandSender.writeDataAndWaitForAck(
// This command updates device's advertised location
// Fire-and-forget - no ACK needed since actual broadcast happens via sendSelfAdvert
await _commandSender.writeData(
FrameBuilder.buildSetAdvertLatLon(
latitude: latitude,
longitude: longitude,