chore: add build artifacts, map assets, and routing data
This commit is contained in:
@@ -9,6 +9,7 @@ import '../services/turn_by_turn_navigation_engine.dart';
|
||||
import '../widgets/active_navigation_hud.dart';
|
||||
import '../widgets/camera_resection_view.dart';
|
||||
import '../widgets/interactive_map_picker_hud.dart';
|
||||
import '../widgets/optical_rangefinder_hud.dart';
|
||||
import '../widgets/place_search_sheet.dart';
|
||||
import '../widgets/tactical_artillery_sheet.dart';
|
||||
import '../widgets/tactical_drawer.dart';
|
||||
@@ -19,9 +20,14 @@ import '../widgets/tactical_los_sheet.dart';
|
||||
import '../widgets/tactical_minefield_sheet.dart';
|
||||
import '../widgets/tactical_overlays_sheet.dart';
|
||||
import '../widgets/tactical_route_planner_sheet.dart';
|
||||
import '../widgets/tactical_route_preview_card.dart';
|
||||
import '../widgets/tactical_symbols_sheet.dart';
|
||||
import '../widgets/tactical_viewshed_sheet.dart';
|
||||
|
||||
import '../models/tactical_sheet_type.dart';
|
||||
import '../widgets/persistent_tactical_sheet_wrapper.dart';
|
||||
|
||||
|
||||
/// ============================================================================
|
||||
/// [TacticalMapScreen] - الشاشة التكتيكية الرئيسية (GetView Architecture)
|
||||
/// ============================================================================
|
||||
@@ -53,6 +59,7 @@ class TacticalMapScreen extends GetView<TacticalMapController> {
|
||||
currentAngleUnit: controller.angleUnit.value,
|
||||
onAngleUnitChanged: controller.setAngleUnit,
|
||||
onOpenResectionHud: () => controller.switchMode('resection_cam'),
|
||||
onStartRangefinderMode: () => controller.switchMode('rangefinder'),
|
||||
onStartRoutingMode: () => _openRoutePlanner(context),
|
||||
onStartLosMode: () {
|
||||
controller.switchMode('los');
|
||||
@@ -87,6 +94,16 @@ class TacticalMapScreen extends GetView<TacticalMapController> {
|
||||
_openOverlaysSheet(context);
|
||||
},
|
||||
onLandmarksSynced: () => controller.update(),
|
||||
onClearMap: () {
|
||||
controller.clearAllTacticalLayers();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
backgroundColor: Color(0xFF0F172A),
|
||||
content: Text('🧹 تم مسح كافة الرسوم والأدوات وتصفير الخريطة بنجاح.'),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
},
|
||||
showContours: controller.showContours.value,
|
||||
onToggleContours: controller.toggleContours,
|
||||
),
|
||||
@@ -102,15 +119,22 @@ class TacticalMapScreen extends GetView<TacticalMapController> {
|
||||
return IntaleqMap(
|
||||
apiKey: AppConfig.apiKey,
|
||||
initialCameraPosition: CameraPosition(
|
||||
target: controller.currentCameraCenter.value,
|
||||
zoom: 13.0,
|
||||
target: controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
||||
zoom: 14.0,
|
||||
),
|
||||
styleUrl: controller.showContours.value
|
||||
? 'assets/style_dark.json'
|
||||
: 'assets/style.json',
|
||||
styleUrl: controller.showContours.value
|
||||
? 'assets/tactical-style-contours.json'
|
||||
: 'assets/tactical-style.json',
|
||||
onMapCreated: (ctrl) {
|
||||
controller.mapController = ctrl;
|
||||
},
|
||||
onStyleLoaded: () {
|
||||
if (controller.currentGpsPosition.value != null) {
|
||||
controller.mapController?.animateCamera(
|
||||
CameraUpdate.newLatLngZoom(controller.currentGpsPosition.value!, 14.0),
|
||||
);
|
||||
}
|
||||
},
|
||||
onCameraMove: (pos) {
|
||||
controller.currentCameraCenter.value = pos.target;
|
||||
},
|
||||
@@ -121,70 +145,98 @@ class TacticalMapScreen extends GetView<TacticalMapController> {
|
||||
);
|
||||
}),
|
||||
|
||||
// ── 2. Top Tactical Operations Header Bar ──────────────
|
||||
Positioned(
|
||||
top: 48,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF090E17).withAlpha(220),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: const Color(0xFF0071E3).withAlpha(120)),
|
||||
// ── 2. Top Operations Floating Bar ────────────────────
|
||||
if (!navState.isNavigating)
|
||||
Positioned(
|
||||
top: 48,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF090E17).withAlpha(220),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: const Color(0xFF0071E3).withAlpha(120)),
|
||||
),
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.menu, color: Color(0xFF38BDF8)),
|
||||
onPressed: () => scaffoldKey.currentState?.openDrawer(),
|
||||
),
|
||||
),
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.menu, color: Color(0xFF38BDF8)),
|
||||
onPressed: () => scaffoldKey.currentState?.openDrawer(),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 48,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF090E17).withAlpha(220),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.white12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.shield,
|
||||
color: Color(0xFF0071E3), size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Obx(
|
||||
() => Text(
|
||||
controller.getModeTitle(),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12.5,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 48,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF090E17).withAlpha(220),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.white12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.shield,
|
||||
color: Color(0xFF0071E3), size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Obx(
|
||||
() => Text(
|
||||
controller.getModeTitle(),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.cleaning_services_rounded,
|
||||
color: Color(0xFFF43F5E), size: 20),
|
||||
tooltip: 'مسح وتنظيف الخريطة',
|
||||
onPressed: () {
|
||||
controller.clearAllTacticalLayers();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
backgroundColor: Color(0xFF0F172A),
|
||||
content: Text('🧹 تم مسح كافة الرسوم والأدوات وتصفير الخريطة بنجاح.'),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF090E17).withAlpha(220),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.white12),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF090E17).withAlpha(220),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.white12),
|
||||
),
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.search,
|
||||
color: Color(0xFF38BDF8)),
|
||||
onPressed: () => _openPlaceSearch(context),
|
||||
),
|
||||
),
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.search,
|
||||
color: Color(0xFF38BDF8)),
|
||||
onPressed: () => _openPlaceSearch(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// ── 3. Interactive Crosshair Target Picker HUD ─────────
|
||||
Obx(() {
|
||||
@@ -210,7 +262,12 @@ class TacticalMapScreen extends GetView<TacticalMapController> {
|
||||
return CameraResectionView(
|
||||
observations: controller.resection.observations,
|
||||
angleUnit: controller.angleUnit.value,
|
||||
initialSelectedLandmark: controller.resection.selectedMapLandmark.value,
|
||||
onObservationAdded: controller.resection.addObservation,
|
||||
onPickFromMap: () {
|
||||
controller.switchMode('nav');
|
||||
controller.setPickerTarget(MapPickerTarget.resectionLandmark);
|
||||
},
|
||||
onCalculatePressed: () {
|
||||
final res = controller.resection.computePosition();
|
||||
if (res != null) {
|
||||
@@ -226,18 +283,247 @@ class TacticalMapScreen extends GetView<TacticalMapController> {
|
||||
);
|
||||
}),
|
||||
|
||||
// ── 4b. Optical Rangefinder Camera HUD (قياس المدى البصري) ───
|
||||
Obx(() {
|
||||
if (controller.currentTacticalMode.value != 'rangefinder') {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return OpticalRangefinderHud(
|
||||
angleUnit: controller.angleUnit.value,
|
||||
onClose: () => controller.switchMode('nav'),
|
||||
onRangeLocked: (dist, heading, pitch) {
|
||||
controller.switchMode('nav');
|
||||
},
|
||||
);
|
||||
}),
|
||||
|
||||
|
||||
// ── 4c. Persistent Tactical Sheet ──────────────────────────
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Obx(() {
|
||||
final activeSheet = controller.activeSheet.value;
|
||||
if (activeSheet == null || navState.isNavigating) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
Widget sheetContent = const SizedBox.shrink();
|
||||
switch (activeSheet.type) {
|
||||
case TacticalSheetType.route:
|
||||
sheetContent = TacticalRoutePlannerSheet(
|
||||
initialOrigin: controller.pickedRouteOrigin.value,
|
||||
initialDestination: controller.pickedRouteDestination.value,
|
||||
currentGps: controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
||||
resectionFix: controller.resection.resectionResult.value != null
|
||||
? LatLng(controller.resection.resectionResult.value!.lat, controller.resection.resectionResult.value!.lng)
|
||||
: null,
|
||||
onRouteConfirmed: (origin, destination, profile, autoStartNav) async {
|
||||
final plan = await controller.navigation.calculateRoute(
|
||||
origin: origin,
|
||||
destination: destination,
|
||||
profile: profile,
|
||||
autoStart: autoStartNav,
|
||||
mapController: controller.mapController,
|
||||
);
|
||||
if (plan != null && plan.polylinePoints.isNotEmpty) {
|
||||
controller.fitBounds(plan.polylinePoints);
|
||||
}
|
||||
},
|
||||
onPickOriginOnMap: () {
|
||||
controller.isSheetMinimized.value = true;
|
||||
controller.setPickerTarget(MapPickerTarget.routeOrigin);
|
||||
},
|
||||
onPickDestinationOnMap: () {
|
||||
controller.isSheetMinimized.value = true;
|
||||
controller.setPickerTarget(MapPickerTarget.routeDestination);
|
||||
},
|
||||
onClose: controller.closeSheet,
|
||||
);
|
||||
break;
|
||||
case TacticalSheetType.los:
|
||||
sheetContent = TacticalLosSheet(
|
||||
observer: controller.los.observerPosition.value ?? const LatLng(31.9539, 35.9106),
|
||||
target: controller.los.targetPosition.value ?? const LatLng(31.9800, 35.9500),
|
||||
currentGps: controller.currentGpsPosition.value,
|
||||
angleUnit: controller.angleUnit.value,
|
||||
onUpdateLocations: (newObs, newTgt) {
|
||||
controller.los.setObserver(newObs);
|
||||
controller.los.setTarget(newTgt);
|
||||
controller.fitBounds([newObs, newTgt]);
|
||||
},
|
||||
onVisibilityChanged: (_) {},
|
||||
onReportGenerated: (_) {},
|
||||
onPickObserverOnMap: () {
|
||||
controller.isSheetMinimized.value = true;
|
||||
controller.setPickerTarget(MapPickerTarget.losObserver);
|
||||
},
|
||||
onPickTargetOnMap: () {
|
||||
controller.isSheetMinimized.value = true;
|
||||
controller.setPickerTarget(MapPickerTarget.losTarget);
|
||||
},
|
||||
onClose: () {
|
||||
controller.closeSheet();
|
||||
controller.switchMode('nav');
|
||||
},
|
||||
);
|
||||
break;
|
||||
case TacticalSheetType.viewshed:
|
||||
sheetContent = TacticalViewshedSheet(
|
||||
observer: controller.viewshed.observerPosition.value ?? controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
||||
currentGps: controller.currentGpsPosition.value,
|
||||
onUpdateObserver: controller.viewshed.setObserver,
|
||||
onReportUpdated: (_) {},
|
||||
onPickObserverOnMap: () {
|
||||
controller.isSheetMinimized.value = true;
|
||||
controller.setPickerTarget(MapPickerTarget.viewshedCenter);
|
||||
},
|
||||
onClose: controller.closeSheet,
|
||||
);
|
||||
break;
|
||||
case TacticalSheetType.artillery:
|
||||
sheetContent = TacticalArtillerySheet(
|
||||
gunPosition: controller.artillery.gunPosition.value ?? controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
||||
targetPosition: controller.artillery.targetPosition.value,
|
||||
angleUnit: controller.angleUnit.value,
|
||||
onPickGun: () {
|
||||
controller.isSheetMinimized.value = true;
|
||||
controller.setPickerTarget(MapPickerTarget.artilleryGun);
|
||||
},
|
||||
onPickTarget: () {
|
||||
controller.isSheetMinimized.value = true;
|
||||
controller.setPickerTarget(MapPickerTarget.artilleryTarget);
|
||||
},
|
||||
onSwap: controller.artillery.swapPositions,
|
||||
onSolutionCalculated: (sol) {
|
||||
controller.fitBounds([sol.gunPosition, sol.targetPosition]);
|
||||
},
|
||||
onClose: controller.closeSheet,
|
||||
);
|
||||
break;
|
||||
case TacticalSheetType.hlz:
|
||||
sheetContent = TacticalHlzSheet(
|
||||
selectedPosition: controller.hlz.selectedPosition.value ?? controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
||||
onPickLocation: () {
|
||||
controller.isSheetMinimized.value = true;
|
||||
controller.setPickerTarget(MapPickerTarget.hlzCenter);
|
||||
},
|
||||
onAssessmentCompleted: (_) {},
|
||||
onClose: controller.closeSheet,
|
||||
);
|
||||
break;
|
||||
case TacticalSheetType.minefield:
|
||||
sheetContent = TacticalMinefieldSheet(
|
||||
startPoint: controller.minefield.startPoint.value ?? controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
||||
endPoint: controller.minefield.endPoint.value,
|
||||
onPickStart: () {
|
||||
controller.isSheetMinimized.value = true;
|
||||
controller.setPickerTarget(MapPickerTarget.minefieldStart);
|
||||
},
|
||||
onPickEnd: () {
|
||||
controller.isSheetMinimized.value = true;
|
||||
controller.setPickerTarget(MapPickerTarget.minefieldEnd);
|
||||
},
|
||||
onSwap: controller.minefield.swapPoints,
|
||||
onZoneCalculated: (res) {
|
||||
controller.fitBounds([res.startPoint, res.endPoint]);
|
||||
},
|
||||
onClose: controller.closeSheet,
|
||||
);
|
||||
break;
|
||||
case TacticalSheetType.isochrone:
|
||||
sheetContent = TacticalIsochroneSheet(
|
||||
center: controller.isochrone.center.value ?? controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
||||
onPickCenter: () {
|
||||
controller.isSheetMinimized.value = true;
|
||||
controller.setPickerTarget(MapPickerTarget.isochroneCenter);
|
||||
},
|
||||
onIsochronesCalculated: (_) {},
|
||||
onClose: controller.closeSheet,
|
||||
);
|
||||
break;
|
||||
case TacticalSheetType.symbols:
|
||||
sheetContent = TacticalSymbolsSheet(
|
||||
activePlacementType: controller.symbols.activePlacementType.value,
|
||||
placedSymbols: controller.symbols.placedSymbols,
|
||||
onSelectSymbolType: (type) {
|
||||
controller.symbols.selectSymbolForPlacement(type);
|
||||
controller.isSheetMinimized.value = true;
|
||||
Get.snackbar(
|
||||
'الرموز العسكرية',
|
||||
'انقر على الخريطة لتثبيت الرمز في الميدان 📍',
|
||||
backgroundColor: const Color(0xFF0071E3),
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
},
|
||||
onDeleteSymbol: (s) => controller.symbols.removeSymbol(s.id),
|
||||
onClose: controller.closeSheet,
|
||||
);
|
||||
break;
|
||||
case TacticalSheetType.overlays:
|
||||
sheetContent = TacticalOverlaysSheet(
|
||||
layers: controller.overlays.overlayLayers,
|
||||
onToggleLayer: controller.overlays.toggleLayer,
|
||||
onClose: controller.closeSheet,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return PersistentTacticalSheetWrapper(
|
||||
title: activeSheet.title,
|
||||
icon: activeSheet.icon,
|
||||
isMinimized: controller.isSheetMinimized.value,
|
||||
onToggleMinimize: () => controller.isSheetMinimized.toggle(),
|
||||
onClose: controller.closeSheet,
|
||||
child: sheetContent,
|
||||
);
|
||||
}),
|
||||
),
|
||||
// ── 5. Active Turn-by-Turn Navigation HUD ──────────────
|
||||
if (navState.isNavigating)
|
||||
|
||||
if (navState.isNavigating) ...[
|
||||
Positioned(
|
||||
top: 48,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: ActiveNavigationHud(
|
||||
state: navState,
|
||||
angleUnit: controller.angleUnit.value,
|
||||
child: ActiveNavigationTopBanner(navState: navState),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 20,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: ActiveNavigationBottomHUD(
|
||||
navState: navState,
|
||||
onStopNavigation: controller.navigation.stopNavigation,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
// ── 6. Tactical Route Preview Card ────────────────────
|
||||
// Positioned خارج Obx إلزامياً: Positioned يجب أن يكون ابناً
|
||||
// مباشراً لـ Stack وإلا فشل التخطيط وhit-test.
|
||||
Positioned(
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: Obx(() {
|
||||
final route = controller.navigation.activeRoute.value;
|
||||
if (route == null || navState.isNavigating) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return TacticalRoutePreviewCard(
|
||||
activeRoute: route,
|
||||
onClose: () => controller.navigation.reset(),
|
||||
onStartNavigation: () {
|
||||
controller.navigation.startNavigation(
|
||||
mapController: controller.mapController,
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -264,236 +550,39 @@ class TacticalMapScreen extends GetView<TacticalMapController> {
|
||||
}
|
||||
|
||||
void _openRoutePlanner(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (ctx) => TacticalRoutePlannerSheet(
|
||||
currentGps: controller.currentGpsPosition.value ??
|
||||
const LatLng(31.9539, 35.9106),
|
||||
resectionFix: controller.resection.resectionResult.value != null
|
||||
? LatLng(controller.resection.resectionResult.value!.lat,
|
||||
controller.resection.resectionResult.value!.lng)
|
||||
: null,
|
||||
onRouteConfirmed: (origin, destination, profile, autoStartNav) {
|
||||
controller.navigation.calculateRoute(
|
||||
origin: origin,
|
||||
destination: destination,
|
||||
profile: profile,
|
||||
autoStart: autoStartNav,
|
||||
mapController: controller.mapController,
|
||||
);
|
||||
},
|
||||
onPickOriginOnMap: () =>
|
||||
controller.setPickerTarget(MapPickerTarget.routeOrigin),
|
||||
onPickDestinationOnMap: () =>
|
||||
controller.setPickerTarget(MapPickerTarget.routeDestination),
|
||||
onClose: () => Navigator.pop(ctx),
|
||||
),
|
||||
);
|
||||
controller.openSheet(ActiveTacticalSheet(TacticalSheetType.route, 'مخطط مسار القوافل', Icons.route));
|
||||
}
|
||||
|
||||
void _openLosSheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (ctx) => Obx(
|
||||
() => TacticalLosSheet(
|
||||
observer: controller.los.observerPosition.value ??
|
||||
const LatLng(31.9539, 35.9106),
|
||||
target: controller.los.targetPosition.value ??
|
||||
const LatLng(31.9800, 35.9500),
|
||||
currentGps: controller.currentGpsPosition.value,
|
||||
angleUnit: controller.angleUnit.value,
|
||||
onUpdateLocations: (newObs, newTgt) {
|
||||
controller.los.setObserver(newObs);
|
||||
controller.los.setTarget(newTgt);
|
||||
controller.fitBounds([newObs, newTgt]);
|
||||
},
|
||||
onVisibilityChanged: (_) {},
|
||||
onReportGenerated: (_) {},
|
||||
onPickObserverOnMap: () {
|
||||
Navigator.pop(context);
|
||||
controller.setPickerTarget(MapPickerTarget.losObserver);
|
||||
},
|
||||
onPickTargetOnMap: () {
|
||||
Navigator.pop(context);
|
||||
controller.setPickerTarget(MapPickerTarget.losTarget);
|
||||
},
|
||||
onClose: () {
|
||||
Navigator.pop(context);
|
||||
controller.switchMode('nav');
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
controller.openSheet(ActiveTacticalSheet(TacticalSheetType.los, 'تحليل خط الرؤية (LOS)', Icons.visibility));
|
||||
}
|
||||
|
||||
void _openViewshed360Sheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (ctx) => Obx(
|
||||
() => TacticalViewshedSheet(
|
||||
observer: controller.viewshed.observerPosition.value ??
|
||||
controller.currentGpsPosition.value ??
|
||||
const LatLng(31.9539, 35.9106),
|
||||
currentGps: controller.currentGpsPosition.value,
|
||||
onUpdateObserver: controller.viewshed.setObserver,
|
||||
onReportUpdated: (_) {},
|
||||
onPickObserverOnMap: () {
|
||||
Navigator.pop(context);
|
||||
controller.setPickerTarget(MapPickerTarget.viewshedCenter);
|
||||
},
|
||||
onClose: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
controller.openSheet(ActiveTacticalSheet(TacticalSheetType.viewshed, 'رادار الرؤية 360 درجة', Icons.radar));
|
||||
}
|
||||
|
||||
void _openArtillerySheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (ctx) => Obx(
|
||||
() => TacticalArtillerySheet(
|
||||
gunPosition: controller.artillery.gunPosition.value ??
|
||||
controller.currentGpsPosition.value ??
|
||||
const LatLng(31.9539, 35.9106),
|
||||
targetPosition: controller.artillery.targetPosition.value,
|
||||
angleUnit: controller.angleUnit.value,
|
||||
onPickGun: () {
|
||||
Navigator.pop(context);
|
||||
controller.setPickerTarget(MapPickerTarget.artilleryGun);
|
||||
},
|
||||
onPickTarget: () {
|
||||
Navigator.pop(context);
|
||||
controller.setPickerTarget(MapPickerTarget.artilleryTarget);
|
||||
},
|
||||
onSwap: controller.artillery.swapPositions,
|
||||
onSolutionCalculated: (sol) {
|
||||
controller.fitBounds([sol.gunPosition, sol.targetPosition]);
|
||||
},
|
||||
onClose: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
controller.openSheet(ActiveTacticalSheet(TacticalSheetType.artillery, 'حاسبة المدفعية الميدانية', Icons.track_changes));
|
||||
}
|
||||
|
||||
void _openHlzSheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (ctx) => Obx(
|
||||
() => TacticalHlzSheet(
|
||||
selectedPosition: controller.hlz.selectedPosition.value ??
|
||||
controller.currentGpsPosition.value ??
|
||||
const LatLng(31.9539, 35.9106),
|
||||
onPickLocation: () {
|
||||
Navigator.pop(context);
|
||||
controller.setPickerTarget(MapPickerTarget.hlzCenter);
|
||||
},
|
||||
onAssessmentCompleted: (_) {},
|
||||
onClose: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
controller.openSheet(ActiveTacticalSheet(TacticalSheetType.hlz, 'استطلاع المهابط (HLZ)', Icons.local_airport));
|
||||
}
|
||||
|
||||
void _openMinefieldSheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (ctx) => Obx(
|
||||
() => TacticalMinefieldSheet(
|
||||
startPoint: controller.minefield.startPoint.value ??
|
||||
controller.currentGpsPosition.value ??
|
||||
const LatLng(31.9539, 35.9106),
|
||||
endPoint: controller.minefield.endPoint.value,
|
||||
onPickStart: () {
|
||||
Navigator.pop(context);
|
||||
controller.setPickerTarget(MapPickerTarget.minefieldStart);
|
||||
},
|
||||
onPickEnd: () {
|
||||
Navigator.pop(context);
|
||||
controller.setPickerTarget(MapPickerTarget.minefieldEnd);
|
||||
},
|
||||
onSwap: controller.minefield.swapPoints,
|
||||
onZoneCalculated: (res) {
|
||||
controller.fitBounds([res.startPoint, res.endPoint]);
|
||||
},
|
||||
onClose: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
controller.openSheet(ActiveTacticalSheet(TacticalSheetType.minefield, 'تأمين الممرات والألغام', Icons.warning_amber_rounded));
|
||||
}
|
||||
|
||||
void _openIsochroneSheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (ctx) => Obx(
|
||||
() => TacticalIsochroneSheet(
|
||||
center: controller.isochrone.center.value ??
|
||||
controller.currentGpsPosition.value ??
|
||||
const LatLng(31.9539, 35.9106),
|
||||
onPickCenter: () {
|
||||
Navigator.pop(context);
|
||||
controller.setPickerTarget(MapPickerTarget.isochroneCenter);
|
||||
},
|
||||
onIsochronesCalculated: (_) {},
|
||||
onClose: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
controller.openSheet(ActiveTacticalSheet(TacticalSheetType.isochrone, 'نطاقات الحركة والإمداد', Icons.speed));
|
||||
}
|
||||
|
||||
void _openSymbolsSheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (ctx) => Obx(
|
||||
() => TacticalSymbolsSheet(
|
||||
activePlacementType: controller.symbols.activePlacementType.value,
|
||||
placedSymbols: controller.symbols.placedSymbols,
|
||||
onSelectSymbolType: (type) {
|
||||
controller.symbols.selectSymbolForPlacement(type);
|
||||
Navigator.pop(context);
|
||||
Get.snackbar(
|
||||
'الرموز العسكرية',
|
||||
'انقر على الخريطة لتثبيت الرمز في الميدان 📍',
|
||||
backgroundColor: const Color(0xFF0071E3),
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
},
|
||||
onDeleteSymbol: (s) => controller.symbols.removeSymbol(s.id),
|
||||
onClose: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
controller.openSheet(ActiveTacticalSheet(TacticalSheetType.symbols, 'الرموز العسكرية (MIL-STD)', Icons.category));
|
||||
}
|
||||
|
||||
void _openOverlaysSheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (ctx) => Obx(
|
||||
() => TacticalOverlaysSheet(
|
||||
layers: controller.overlays.overlayLayers,
|
||||
onToggleLayer: controller.overlays.toggleLayer,
|
||||
onClose: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
controller.openSheet(ActiveTacticalSheet(TacticalSheetType.overlays, 'شفافات العمليات (IPB)', Icons.layers));
|
||||
}
|
||||
|
||||
void _openPlaceSearch(BuildContext context) {
|
||||
|
||||
Reference in New Issue
Block a user