Új appbar, NtripSettingsSheet

This commit is contained in:
2026-05-27 15:04:46 +02:00
parent ce8b539be3
commit 3d4c937b71
13 changed files with 789 additions and 67 deletions
+92 -2
View File
@@ -19,8 +19,8 @@ import 'gnss_device_picker_dialog.dart';
/// - Kék: DGPS (quality 2)
/// - Világoszöld: RTK Float (quality 5)
/// - Zöld: RTK Fix (quality 4) ← ideális állapot
class GnssStatusChip extends StatelessWidget {
const GnssStatusChip({super.key});
class GnssIconStatusChip extends StatelessWidget {
const GnssIconStatusChip({super.key});
@override
Widget build(BuildContext context) {
@@ -84,6 +84,61 @@ class GnssStatusChip extends StatelessWidget {
}
}
class GnssTextStatusChip extends StatelessWidget {
const GnssTextStatusChip({super.key});
@override
Widget build(BuildContext context) {
return Obx(() {
final connState = GnssService.to.connectionState.value;
final quality = GnssService.to.gpsQuality.value;
final isConn = connState == GnssConnectionState.connected;
final isConnecting = connState == GnssConnectionState.connecting;
final color = _chipColor(isConn, quality);
return isConnecting
? Text('')
: Text(_chipLabel(connState, quality, ''),
style: TextStyle(
fontSize: 12, fontWeight: FontWeight.w600, color: color));
});
}
Color _chipColor(bool connected, int quality) {
if (!connected) return Colors.grey;
return switch (quality) {
4 => Colors.greenAccent,
5 => Colors.lightGreen,
2 => Colors.blue,
1 => Colors.orange,
_ => Colors.orange.shade300,
};
}
IconData _chipIcon(bool connected, int quality) {
if (!connected) return Icons.gps_off;
if (quality == 0) return Icons.gps_not_fixed;
return Icons.gps_fixed;
}
String _chipLabel(
GnssConnectionState state, int quality, String? deviceName) {
return switch (state) {
GnssConnectionState.connecting => 'Kapcsolódás...',
GnssConnectionState.disconnected =>
deviceName != null ? '-----' : 'N-----z',
GnssConnectionState.error => 'Hiba',
GnssConnectionState.connected => switch (quality) {
4 => 'RTK Fix',
5 => 'RTK Float',
2 => 'DGPS',
1 => 'GPS',
_ => 'Fix nélkül',
},
};
}
}
/// NTRIP kapcsolat chip az AppBar-ban.
///
/// Tapintásra az NTRIP beállítások oldalra navigál.
@@ -143,3 +198,38 @@ class NtripStatusChip extends StatelessWidget {
});
}
}
class NtripIconStatusChip extends StatelessWidget {
/// NTRIP csatlakozva van-e — a controllerből kapja.
final RxBool isConnected;
/// Csatlakozás / leválasztás callback.
final VoidCallback? onToggle;
/// NTRIP beállítások megnyitása.
final VoidCallback? onSettings;
const NtripIconStatusChip({
super.key,
required this.isConnected,
this.onToggle,
this.onSettings,
});
@override
Widget build(BuildContext context) {
return Obx(() {
final connected = isConnected.value;
final color = connected ? Colors.greenAccent : Colors.grey;
return GestureDetector(
onTap: onToggle,
onLongPress: onSettings,
child: Icon(
Icons.cell_tower,
color: color,
),
);
});
}
}
+1
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:terepi_seged/enums/map_survey_mode.dart';
import 'package:terepi_seged/pages/map_survey/presentations/controllers/map_survey_controller.dart';
import 'package:terepi_seged/pages/shell/presentations/controllers/shell_controller.dart';
+90
View File
@@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'package:get/get_state_manager/get_state_manager.dart';
import 'package:terepi_seged/enums/map_survey_mode.dart';
import 'package:terepi_seged/pages/map_survey/presentations/controllers/map_survey_controller.dart';
class MapModeMenuAnchor extends StatelessWidget {
final MapSurveyController controller;
const MapModeMenuAnchor({super.key, required this.controller});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Obx(() {
return MenuAnchor(
menuChildren: [
MenuItemButton(
leadingIcon: const Icon(Icons.map),
trailingIcon: controller.mode.value == MapSurveyMode.browse
? const Icon(Icons.check)
: null,
onPressed: () => controller.setMode(MapSurveyMode.browse),
child: const Text('Térkép')),
MenuItemButton(
leadingIcon: const Icon(Icons.add_location_alt),
trailingIcon: controller.mode.value == MapSurveyMode.measure
? const Icon(Icons.check)
: null,
onPressed: () => controller.setMode(MapSurveyMode.measure),
child: const Text('Bemérés')),
MenuItemButton(
leadingIcon: const Icon(Icons.gps_fixed),
trailingIcon: controller.mode.value == MapSurveyMode.stakeout
? const Icon(Icons.check)
: null,
onPressed: () => controller.setMode(MapSurveyMode.stakeout),
child: const Text('Kitűzés')),
MenuItemButton(
leadingIcon: const Icon(Icons.hiking),
trailingIcon: controller.mode.value == MapSurveyMode.fieldWalk
? const Icon(Icons.check)
: null,
onPressed: () => controller.setMode(MapSurveyMode.fieldWalk),
child: const Text('Bejárás')),
MenuItemButton(
leadingIcon: const Icon(Icons.route),
trailingIcon: controller.mode.value == MapSurveyMode.browse
? const Icon(Icons.check)
: null,
onPressed: () => controller.setMode(MapSurveyMode.browse),
child: const Text('Track')),
],
builder: (context, menuController, child) {
return InkWell(
borderRadius: BorderRadius.circular(18),
onTap: () {
if (menuController.isOpen) {
menuController.close();
} else {
menuController.open();
}
},
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 4,
),
child: Column(
children: [
Row(mainAxisSize: MainAxisSize.min, children: [
Icon(controller.currentModeIcon, size: 14),
const SizedBox(width: 1),
Text(controller.currentModeLabel,
style: const TextStyle(
fontSize: 14, fontWeight: FontWeight.w600)),
const Icon(Icons.arrow_drop_down, size: 18),
]),
Text('Projekt',
style: TextStyle(
fontSize: 11,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w400,
color: colorScheme.onSurfaceVariant))
],
)));
});
});
}
}
+22
View File
@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:terepi_seged/pages/shell/presentations/controllers/shell_controller.dart';
class ShellAdaptiveAppBar extends StatelessWidget
implements PreferredSizeWidget {
final ShellController controller;
const ShellAdaptiveAppBar({
super.key,
required this.controller,
});
@override
Size get preferredSize => const Size.fromHeight(60);
@override
Widget build(BuildContext context) {
return AppBar(
title: const Text('Adatook'),
);
}
}
+131
View File
@@ -0,0 +1,131 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get_state_manager/get_state_manager.dart';
import 'package:get/state_manager.dart';
import 'package:terepi_seged/pages/map_survey/presentations/controllers/map_survey_controller.dart';
import 'package:terepi_seged/pages/shell/presentations/controllers/shell_controller.dart';
import 'package:terepi_seged/services/ntrip_service.dart';
import 'package:terepi_seged/widgets/gnss_status_chip.dart';
import 'package:terepi_seged/widgets/map_mode_menu_anchor.dart';
class ShellMapAppBar extends StatelessWidget implements PreferredSizeWidget {
final MapSurveyController controller;
const ShellMapAppBar({
super.key,
required this.controller,
});
@override
Size get preferredSize => const Size.fromHeight(52);
@override
Widget build(BuildContext context) {
return AppBar(
toolbarHeight: 60,
leadingWidth: 44,
titleSpacing: 0,
leading: Builder(builder: (context) {
return IconButton(
padding: EdgeInsets.zero,
constraints: const BoxConstraints(minWidth: 40, minHeight: 40),
icon: const Icon(Icons.menu),
onPressed: () {
Scaffold.of(context).openDrawer();
},
);
}),
title: Obx(() {
return Row(children: [
MapModeMenuAnchor(controller: controller),
const SizedBox(width: 2),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(children: [
const Text('H:', style: TextStyle(fontSize: 12)),
SizedBox(width: 2),
Text(controller.horizontalAccuracyText,
style: TextStyle(
fontSize: 12,
color: _errorColor(max(
controller.gpsLatitudeError.value,
controller.gpsLongitudeError.value)),
fontWeight: FontWeight.w600,
fontFeatures: const [FontFeature.tabularFigures()]))
]),
Row(children: [
const Text('V:', style: TextStyle(fontSize: 12)),
SizedBox(width: 2),
Text(controller.verticalAccuracyText,
style: TextStyle(
fontSize: 12,
color:
_errorColor(controller.gpsAltitudeError.value),
fontWeight: FontWeight.w600,
fontFeatures: const [FontFeature.tabularFigures()]))
])
]),
SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
GnssTextStatusChip(),
Row(children: [
const Text('V:', style: TextStyle(fontSize: 12)),
SizedBox(width: 2),
Text(controller.verticalAccuracyText,
style: TextStyle(
fontSize: 12,
color:
_errorColor(controller.gpsAltitudeError.value),
fontWeight: FontWeight.w600,
fontFeatures: const [FontFeature.tabularFigures()]))
])
]),
const GnssIconStatusChip(),
const SizedBox(width: 2),
NtripIconStatusChip(
isConnected: NtripService.to.isConnected,
onToggle: () {
NtripService.to.isConnected.value
? NtripService.to.disconnect()
: NtripService.to.connect();
},
onSettings: () {
HapticFeedback.mediumImpact();
controller.openNtripsettings();
}),
]);
}),
actions: [
PopupMenuButton(
tooltip: 'További funkciók',
icon: const Icon(Icons.more_vert),
onSelected: null,
itemBuilder: (context) => const [
PopupMenuItem(
value: 1,
child: Text('Koordináták'),
),
PopupMenuItem(
value: 1,
child: Text('Rétegek'),
),
])
]);
}
Color _errorColor(double e) {
if (e < 0.05) return Colors.greenAccent;
if (e < 0.2) return Colors.green;
if (e < 1.0) return Colors.orange;
return Colors.red;
}
}