// lib/widgets/gnss_device_picker_dialog.dart import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../services/gnss/gnss_device_service.dart'; import '../services/gnss/gnss_service.dart'; class GnssDevicePickerDialog extends StatelessWidget { const GnssDevicePickerDialog({super.key}); static Future show() => Get.dialog( const GnssDevicePickerDialog(), barrierDismissible: true, ); @override Widget build(BuildContext context) { final svc = GnssDeviceService.to; final screenHeight = MediaQuery.of(context).size.height; return Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), // A dialog max a képernyő 80%-át foglalja el child: ConstrainedBox( constraints: BoxConstraints( maxHeight: screenHeight * 0.80, ), child: Padding( padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // ── Fejléc — fix, nem scrollozódik ────────────────── Row(children: [ const Icon(Icons.sensors), const SizedBox(width: 10), const Expanded( child: Text( 'GNSS eszköz kiválasztása', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), ), ), IconButton( icon: const Icon(Icons.close, size: 20), onPressed: () => Get.back(), ), ]), const Divider(height: 24), // ── Scrollozható tartalom ──────────────────────────── Flexible( child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Telefon GPS opció _DeviceTile( device: const GnssDevice( address: 'phone', name: 'Telefon GPS', type: GnssConnectionType.phoneGps, ), ), const SizedBox(height: 12), // BT Serial — párosított eszközök _SectionHeader( title: 'Bluetooth Serial (párosított)', trailing: TextButton.icon( icon: const Icon(Icons.refresh, size: 16), label: const Text('Frissítés'), onPressed: svc.loadPairedBtDevices, ), ), Obx(() { if (svc.pairedBtDevices.isEmpty) { return const Padding( padding: EdgeInsets.symmetric(vertical: 8), child: Text( 'Nincs párosított BT eszköz.', style: TextStyle(color: Colors.grey, fontSize: 13), ), ); } return Column( children: svc.pairedBtDevices .map((d) => _DeviceTile(device: d)) .toList(), ); }), const SizedBox(height: 12), // BLE — keresett eszközök _SectionHeader( title: 'Bluetooth LE', trailing: Obx( () => svc.isScanning.value ? Row( mainAxisSize: MainAxisSize.min, children: [ const SizedBox( width: 14, height: 14, child: CircularProgressIndicator( strokeWidth: 2), ), const SizedBox(width: 6), TextButton( onPressed: svc.stopBleScan, child: const Text('Stop'), ), ], ) : TextButton.icon( icon: const Icon(Icons.search, size: 16), label: const Text('Keresés'), onPressed: svc.startBleScan, ), ), ), Obx(() { if (svc.isScanning.value && svc.scannedBleDevices.isEmpty) { return const Padding( padding: EdgeInsets.symmetric(vertical: 8), child: Text( 'Keresés folyamatban...', style: TextStyle(color: Colors.grey, fontSize: 13), ), ); } if (svc.scannedBleDevices.isEmpty) { return const Padding( padding: EdgeInsets.symmetric(vertical: 8), child: Text( 'Kattints a "Keresés" gombra.', style: TextStyle(color: Colors.grey, fontSize: 13), ), ); } return Column( children: svc.scannedBleDevices .map((d) => _DeviceTile(device: d)) .toList(), ); }), // Kis padding alulra a scrollozás végén const SizedBox(height: 8), ], ), ), ), ], ), ), ), ); } } // ── Eszköz sor ────────────────────────────────────────────────────── class _DeviceTile extends StatelessWidget { final GnssDevice device; const _DeviceTile({required this.device}); @override Widget build(BuildContext context) { final svc = GnssDeviceService.to; return Obx(() { final isSelected = svc.selectedDevice.value?.address == device.address; return ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), leading: CircleAvatar( radius: 18, backgroundColor: isSelected ? Colors.blue.withOpacity(0.15) : Colors.grey.withOpacity(0.1), child: Icon( _iconFor(device.type), size: 18, color: isSelected ? Colors.blue : Colors.grey, ), ), title: Text( device.name, style: TextStyle( fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, fontSize: 14, ), ), subtitle: Row(children: [ _TypeChip(device.typeLabel), if (device.rssi != null) ...[ const SizedBox(width: 6), Text( '${device.rssi} dBm', style: const TextStyle(fontSize: 11, color: Colors.grey), ), ], if (device.type != GnssConnectionType.phoneGps) Padding( padding: const EdgeInsets.only(left: 6), child: Text( device.address, style: const TextStyle(fontSize: 10, color: Colors.grey), ), ), ]), trailing: isSelected ? const Icon(Icons.check_circle, color: Colors.blue) : null, onTap: () async { await svc.selectDevice(device); Get.back(); await GnssService.to.onDeviceChanged(device); }, ); }); } IconData _iconFor(GnssConnectionType type) => switch (type) { GnssConnectionType.btSerial => Icons.bluetooth, GnssConnectionType.ble => Icons.bluetooth_searching, GnssConnectionType.phoneGps => Icons.phone_android, }; } // ── Szekció fejléc ─────────────────────────────────────────────────── class _SectionHeader extends StatelessWidget { final String title; final Widget? trailing; const _SectionHeader({required this.title, this.trailing}); @override Widget build(BuildContext context) { return Row(children: [ Text( title, style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: Colors.grey.shade600, letterSpacing: 0.5, ), ), const Spacer(), if (trailing != null) trailing!, ]); } } // ── Típus chip ─────────────────────────────────────────────────────── class _TypeChip extends StatelessWidget { final String label; const _TypeChip(this.label); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1), decoration: BoxDecoration( color: Colors.blue.withOpacity(0.1), borderRadius: BorderRadius.circular(4), ), child: Text( label, style: const TextStyle(fontSize: 10, color: Colors.blue), ), ); } }