// lib/widgets/save_point_fab.dart import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../services/gnss/gnss_service.dart'; /// Pont mentése FAB + dialóg widget. /// /// A meglévő [MapSurveyController] mentési logikájára épül. /// GPS fix nélkül letiltva — kesztyűvel is jól tapintható méret. /// /// Használat: /// ```dart /// // Stack-ben, jobb alulra pozicionálva: /// Positioned( /// right: 16, /// bottom: 16, /// child: SavePointFab(controller: controller), /// ) /// ``` class SavePointFab extends StatelessWidget { /// A controller dynamic-ként fogadva — így nincs /// közvetlen import kényszer a MapSurveyController-re. final dynamic controller; const SavePointFab({super.key, required this.controller}); @override Widget build(BuildContext context) { return Obx(() { final quality = GnssService.to.gpsQuality.value; final hasfix = quality > 0; final color = _fixColor(quality); return Stack( clipBehavior: Clip.none, children: [ // ── Fő FAB ──────────────────────────────────────────── FloatingActionButton.large( heroTag: 'save_point_fab', tooltip: hasfix ? 'Pont rögzítése' : 'GPS fix szükséges', backgroundColor: hasfix ? Theme.of(context).colorScheme.primaryContainer : Colors.grey.shade700, onPressed: hasfix ? () => _showSaveDialog(context) : null, child: Icon( Icons.flag, size: 34, color: hasfix ? Theme.of(context).colorScheme.onPrimaryContainer : Colors.grey.shade400, ), ), // ── Fix minőség jelző — jobb felső sarok ───────────── Positioned( top: -4, right: -4, child: AnimatedContainer( duration: const Duration(milliseconds: 400), width: 14, height: 14, decoration: BoxDecoration( color: color, shape: BoxShape.circle, border: Border.all(color: Colors.white, width: 2), boxShadow: [ BoxShadow( color: color.withOpacity(0.5), blurRadius: 6, spreadRadius: 1, ), ], ), ), ), ], ); }); } // ── Dialóg ──────────────────────────────────────────────────────── void _showSaveDialog(BuildContext context) { // A controller mezőit frissítjük az aktuális sorszámra controller.pointIdController.text = controller.pointId.toString(); controller.pointDescriptionController.text = ''; controller.gpsHeightController.text = ''; Get.dialog( Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // ── Fejléc ─────────────────────────────────────── Row(children: [ Obx(() { final q = GnssService.to.gpsQuality.value; return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: _fixColor(q).withOpacity(0.15), borderRadius: BorderRadius.circular(6), border: Border.all(color: _fixColor(q).withOpacity(0.5)), ), child: Text( _fixLabel(q), style: TextStyle( color: _fixColor(q), fontSize: 12, fontWeight: FontWeight.w600, ), ), ); }), const SizedBox(width: 10), const Expanded( child: Text( 'Pont rögzítése', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ), IconButton( icon: const Icon(Icons.close, size: 20), onPressed: () => Get.back(), ), ]), const SizedBox(height: 8), // ── EOV előnézet ───────────────────────────────── Obx(() { final fmt = controller.formatEov; final eovY = fmt.format(controller.eov.value.Y); final eovX = fmt.format(controller.eov.value.X); return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Theme.of(context) .colorScheme .surfaceVariant .withOpacity(0.5), borderRadius: BorderRadius.circular(8), ), child: Column( children: [ _PreviewRow('EOV Y', eovY, 'm'), const SizedBox(height: 2), _PreviewRow('EOV X', eovX, 'm'), const SizedBox(height: 2), _PreviewRow( 'H (MSL)', '${(controller.gpsAltitude.value as double).toStringAsFixed(3)}', 'm', ), ], ), ); }), const SizedBox(height: 16), // ── Pont azonosító ─────────────────────────────── TextField( controller: controller.pointIdController, autofocus: true, keyboardType: TextInputType.number, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Pont azonosító', prefixIcon: Icon(Icons.tag, size: 18), ), ), const SizedBox(height: 12), // ── Leírás ─────────────────────────────────────── TextField( controller: controller.pointDescriptionController, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Leírás (opcionális)', prefixIcon: Icon(Icons.notes, size: 18), ), ), const SizedBox(height: 12), // ── Pólus magasság ─────────────────────────────── TextField( controller: controller.gpsHeightController, keyboardType: const TextInputType.numberWithOptions(decimal: true), decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Pólus magasság (m)', prefixIcon: Icon(Icons.height, size: 18), hintText: '0.00', ), ), const SizedBox(height: 20), // ── Gombok ─────────────────────────────────────── Row(children: [ Expanded( child: OutlinedButton( onPressed: () => Get.back(), child: const Text('Mégsem'), ), ), const SizedBox(width: 12), Expanded( flex: 2, child: FilledButton.icon( icon: const Icon(Icons.save_alt, size: 18), label: const Text('Mentés'), onPressed: () async { await _savePoint(); }, ), ), ]), ], ), ), ), barrierDismissible: false, ); } // ── Mentési logika ──────────────────────────────────────────────── Future _savePoint() async { // A meglévő controller logikájának meghívása // (onBottomNavigationBarTap belső logikájából kiemelve) try { controller.pointId = int.tryParse(controller.pointIdController.text) ?? controller.pointId; // A controller meglévő mentési metódusának hívása // Ez tartalmazza: lista, fájl, Supabase mentés, marker await controller.saveCurrentPoint(); Get.back(); Get.snackbar( 'Pont mentve', '${controller.pointIdController.text} sikeresen rögzítve.', backgroundColor: Colors.green.withOpacity(0.9), colorText: Colors.white, duration: const Duration(seconds: 2), snackPosition: SnackPosition.TOP, ); } catch (e) { Get.snackbar( 'Hiba', 'Mentés sikertelen: $e', backgroundColor: Colors.red.withOpacity(0.9), colorText: Colors.white, ); } } } // ── EOV előnézet sor ────────────────────────────────────────────────── class _PreviewRow extends StatelessWidget { final String label, value, unit; const _PreviewRow(this.label, this.value, this.unit); @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: TextStyle( fontSize: 11, color: Theme.of(context) .colorScheme .onSurfaceVariant .withOpacity(0.7), )), Text( '$value $unit', style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w500, fontFeatures: [FontFeature.tabularFigures()], ), ), ], ); } } // ── Segédfüggvények ─────────────────────────────────────────────────── Color _fixColor(int q) => switch (q) { 4 => Colors.greenAccent, 5 => Colors.lightGreen, 2 => Colors.blue, 1 => Colors.orange, _ => Colors.grey, }; String _fixLabel(int q) => switch (q) { 4 => 'RTK Fix', 5 => 'RTK Float', 2 => 'DGPS', 1 => 'GPS', _ => 'Nincs fix', };