Térkép refraktorálás
This commit is contained in:
parent
d50a324e44
commit
ee0f90e247
@ -12,6 +12,7 @@ import 'package:terepi_seged/pages/map_survey/presentations/controllers/map_surv
|
|||||||
import 'package:terepi_seged/pages/map_survey/presentations/views/settings_dialog.dart';
|
import 'package:terepi_seged/pages/map_survey/presentations/views/settings_dialog.dart';
|
||||||
import 'package:terepi_seged/utils/rive_utils.dart';
|
import 'package:terepi_seged/utils/rive_utils.dart';
|
||||||
import 'package:terepi_seged/widgets/coordinate_panel.dart';
|
import 'package:terepi_seged/widgets/coordinate_panel.dart';
|
||||||
|
import 'package:terepi_seged/widgets/map_bottom_panel.dart';
|
||||||
import 'package:terepi_seged/widgets/save_point_fab.dart';
|
import 'package:terepi_seged/widgets/save_point_fab.dart';
|
||||||
import 'package:terepi_seged/widgets/shared_map_widgets.dart';
|
import 'package:terepi_seged/widgets/shared_map_widgets.dart';
|
||||||
|
|
||||||
@ -26,26 +27,31 @@ class MapSurveyView extends GetView<MapSurveyController> {
|
|||||||
const SharedMapWidget(),
|
const SharedMapWidget(),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 8,
|
top: 8,
|
||||||
right: 8,
|
right: 60,
|
||||||
left: 8,
|
left: 8,
|
||||||
child: CoordinatePanel.fromController(controller),
|
child: CoordinatePanel.fromController(controller),
|
||||||
),
|
),
|
||||||
Positioned(top: 8, left: 0, right: 0, child: _ModeSelector()),
|
// Positioned(top: 8, left: 0, right: 0, child: _ModeSelector()),
|
||||||
|
// Positioned(
|
||||||
|
// bottom: 80,
|
||||||
|
// left: 8,
|
||||||
|
// right: 8,
|
||||||
|
// child: Obx(
|
||||||
|
// () => controller.mode.value == MapSurveyMode.stakeout
|
||||||
|
// ? _StakeoutPanel() // ΔY, ΔX, távolság, irányszög
|
||||||
|
// : const SizedBox.shrink(),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// Positioned(
|
||||||
|
// bottom: 16,
|
||||||
|
// right: 16,
|
||||||
|
// child: SavePointFab(controller: controller),
|
||||||
|
// ),
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 80,
|
bottom: 0,
|
||||||
left: 8,
|
left: 0,
|
||||||
right: 8,
|
right: 0,
|
||||||
child: Obx(
|
child: MapBottomPanel(controller: controller))
|
||||||
() => controller.mode.value == MapSurveyMode.stakeout
|
|
||||||
? _StakeoutPanel() // ΔY, ΔX, távolság, irányszög
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
bottom: 16,
|
|
||||||
right: 16,
|
|
||||||
child: SavePointFab(controller: controller),
|
|
||||||
),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ class ShellController extends GetxController {
|
|||||||
static ShellController get to => Get.find();
|
static ShellController get to => Get.find();
|
||||||
|
|
||||||
final currentIndex = 0.obs;
|
final currentIndex = 0.obs;
|
||||||
|
final isNavBarVisible = true.obs;
|
||||||
|
|
||||||
static const titles = [
|
static const titles = [
|
||||||
'Térkép',
|
'Térkép',
|
||||||
@ -24,4 +25,8 @@ class ShellController extends GetxController {
|
|||||||
void goToSurvey() => goToTab(1);
|
void goToSurvey() => goToTab(1);
|
||||||
void goToTracking() => goToTab(2);
|
void goToTracking() => goToTab(2);
|
||||||
void goToData() => goToTab(3);
|
void goToData() => goToTab(3);
|
||||||
|
|
||||||
|
void showNavBar() => isNavBarVisible.value = true;
|
||||||
|
void hideNavBar() => isNavBarVisible.value = false;
|
||||||
|
void toggleNavBar() => isNavBarVisible.value = !isNavBarVisible.value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,30 +46,37 @@ class ShellView extends GetView<ShellController> {
|
|||||||
index: controller.currentIndex.value,
|
index: controller.currentIndex.value,
|
||||||
children: _pages,
|
children: _pages,
|
||||||
)),
|
)),
|
||||||
bottomNavigationBar: Obx(() => NavigationBar(
|
bottomNavigationBar: Obx(
|
||||||
selectedIndex: controller.currentIndex.value,
|
() => AnimatedSize(
|
||||||
onDestinationSelected: controller.goToTab,
|
duration: const Duration(milliseconds: 250),
|
||||||
destinations: const [
|
curve: Curves.easeInOut,
|
||||||
NavigationDestination(
|
child: controller.isNavBarVisible.value
|
||||||
icon: Icon(Icons.map_outlined),
|
? NavigationBar(
|
||||||
selectedIcon: Icon(Icons.map),
|
selectedIndex: controller.currentIndex.value,
|
||||||
label: 'Térkép',
|
onDestinationSelected: controller.goToTab,
|
||||||
),
|
destinations: const [
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: Icon(Icons.gps_fixed),
|
icon: Icon(Icons.map_outlined),
|
||||||
label: 'Mérés',
|
selectedIcon: Icon(Icons.map),
|
||||||
),
|
label: 'Térkép',
|
||||||
NavigationDestination(
|
),
|
||||||
icon: Icon(Icons.route),
|
NavigationDestination(
|
||||||
label: 'Track',
|
icon: Icon(Icons.gps_fixed),
|
||||||
),
|
label: 'Mérés',
|
||||||
NavigationDestination(
|
),
|
||||||
icon: Icon(Icons.table_chart_outlined),
|
NavigationDestination(
|
||||||
selectedIcon: Icon(Icons.table_chart),
|
icon: Icon(Icons.route),
|
||||||
label: 'Adatok',
|
label: 'Track',
|
||||||
),
|
),
|
||||||
],
|
NavigationDestination(
|
||||||
)),
|
icon: Icon(Icons.table_chart_outlined),
|
||||||
|
selectedIcon: Icon(Icons.table_chart),
|
||||||
|
label: 'Adatok',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink()),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,66 +27,23 @@ class GnssStatusChip extends StatelessWidget {
|
|||||||
return Obx(() {
|
return Obx(() {
|
||||||
final connState = GnssService.to.connectionState.value;
|
final connState = GnssService.to.connectionState.value;
|
||||||
final quality = GnssService.to.gpsQuality.value;
|
final quality = GnssService.to.gpsQuality.value;
|
||||||
final device = GnssDeviceService.to.selectedDevice.value;
|
final isConn = connState == GnssConnectionState.connected;
|
||||||
final sats = GnssService.to.satelliteCount.value;
|
|
||||||
|
|
||||||
final isConnected = connState == GnssConnectionState.connected;
|
|
||||||
final isConnecting = connState == GnssConnectionState.connecting;
|
final isConnecting = connState == GnssConnectionState.connecting;
|
||||||
final color = _chipColor(isConnected, quality);
|
final color = _chipColor(isConn, quality);
|
||||||
final label = _chipLabel(connState, quality, device?.name);
|
|
||||||
|
|
||||||
return GestureDetector(
|
return Tooltip(
|
||||||
onTap: () => GnssDevicePickerDialog.show(),
|
// Hosszú nyomásra szöveg jelenik meg
|
||||||
child: AnimatedContainer(
|
message: _chipLabel(connState, quality, ''),
|
||||||
duration: const Duration(milliseconds: 300),
|
child: IconButton(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
icon: isConnecting
|
||||||
decoration: BoxDecoration(
|
? SizedBox(
|
||||||
color: color.withOpacity(0.15),
|
width: 18,
|
||||||
borderRadius: BorderRadius.circular(8),
|
height: 18,
|
||||||
border: Border.all(color: color.withOpacity(0.5)),
|
child:
|
||||||
),
|
CircularProgressIndicator(strokeWidth: 2, color: color),
|
||||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
)
|
||||||
// Állapot ikon
|
: Icon(_chipIcon(isConn, quality), color: color),
|
||||||
if (isConnecting)
|
onPressed: () => GnssDevicePickerDialog.show(),
|
||||||
SizedBox(
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
strokeWidth: 1.5,
|
|
||||||
color: color,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Icon(
|
|
||||||
_chipIcon(isConnected, quality),
|
|
||||||
size: 12,
|
|
||||||
color: color,
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
|
|
||||||
// Felirat
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 11,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Műholdak száma (csak ha van fix)
|
|
||||||
if (isConnected && quality > 0) ...[
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
'($sats)',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
color: color.withOpacity(0.8),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
426
lib/widgets/map_bottom_panel.dart
Normal file
426
lib/widgets/map_bottom_panel.dart
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.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 '../services/gnss/gnss_service.dart';
|
||||||
|
|
||||||
|
/// Összecsukható alsó panel — bemérés és kitűzés módváltóval.
|
||||||
|
///
|
||||||
|
/// Összecsukva: mód chip-ek + kis mentés gomb (nem takarja a térképet).
|
||||||
|
/// Kitűzés módban automatikusan kinyílik a ΔY/ΔX/távolság panellel.
|
||||||
|
///
|
||||||
|
/// Használat a Stack aljában:
|
||||||
|
/// ```dart
|
||||||
|
/// Positioned(
|
||||||
|
/// bottom: 0, left: 0, right: 0,
|
||||||
|
/// child: SurveyBottomPanel(controller: controller),
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
class MapBottomPanel extends StatefulWidget {
|
||||||
|
final dynamic controller;
|
||||||
|
|
||||||
|
const MapBottomPanel({super.key, required this.controller});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MapBottomPanel> createState() => _SurveyBottomPanelState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SurveyBottomPanelState extends State<MapBottomPanel>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController _animCtrl;
|
||||||
|
late Animation<double> _heightAnim;
|
||||||
|
|
||||||
|
// Panel magasságok
|
||||||
|
static const _collapsedHeight = 68.0;
|
||||||
|
static const _expandedHeight = 220.0;
|
||||||
|
|
||||||
|
bool _isExpanded = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_animCtrl = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
);
|
||||||
|
_heightAnim = Tween(
|
||||||
|
begin: _collapsedHeight,
|
||||||
|
end: _expandedHeight,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: _animCtrl,
|
||||||
|
curve: Curves.easeOutCubic,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Ha kitűzés módba lépünk → automatikus kinyitás
|
||||||
|
ever(widget.controller.mode as Rx<MapSurveyMode>, (mode) {
|
||||||
|
if (mode == MapSurveyMode.stakeout.index && !_isExpanded) {
|
||||||
|
_expand();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animCtrl.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toggle() {
|
||||||
|
if (_isExpanded) {
|
||||||
|
_collapse();
|
||||||
|
} else {
|
||||||
|
_expand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _expand() {
|
||||||
|
_animCtrl.forward();
|
||||||
|
setState(() => _isExpanded = true);
|
||||||
|
ShellController.to.hideNavBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _collapse() {
|
||||||
|
_animCtrl.reverse();
|
||||||
|
setState(() => _isExpanded = false);
|
||||||
|
ShellController.to.showNavBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: _heightAnim,
|
||||||
|
builder: (context, child) => Container(
|
||||||
|
height: _heightAnim.value,
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black.withOpacity(0.88),
|
||||||
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.3),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, -2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// ── Handle — húzásra nyíl/csuk ─────────────────────
|
||||||
|
GestureDetector(
|
||||||
|
onTap: _toggle,
|
||||||
|
onVerticalDragEnd: (d) {
|
||||||
|
// Felfelé húzás → kinyit, lefelé → csuk
|
||||||
|
if (d.primaryVelocity! < -100) _expand();
|
||||||
|
if (d.primaryVelocity! > 100) _collapse();
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 20,
|
||||||
|
child: Center(
|
||||||
|
child: Container(
|
||||||
|
width: 36,
|
||||||
|
height: 3,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.25),
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// ── Módváltó + mentés gomb (mindig látható) ─────────
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
10, 6, 10, 6 + MediaQuery.of(context).padding.bottom),
|
||||||
|
child: _ModeRow(
|
||||||
|
controller: widget.controller,
|
||||||
|
onSave: () => _showSaveDialog(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// ── Kitűzési adatok (csak kinyitva) ─────────────────
|
||||||
|
if (_isExpanded && _animCtrl.value > 0.5)
|
||||||
|
Expanded(
|
||||||
|
child: _StakeoutContent(
|
||||||
|
controller: widget.controller,
|
||||||
|
onSave: () => _showSaveDialog(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showSaveDialog(BuildContext context) {
|
||||||
|
widget.controller.showAddPointDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Módváltó sor ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class _ModeRow extends StatelessWidget {
|
||||||
|
final dynamic controller;
|
||||||
|
final VoidCallback onSave;
|
||||||
|
|
||||||
|
const _ModeRow({required this.controller, required this.onSave});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Obx(() {
|
||||||
|
final mode = (controller.mode?.value ?? MapSurveyMode.measure);
|
||||||
|
|
||||||
|
return Row(children: [
|
||||||
|
// Bemérés chip
|
||||||
|
Expanded(
|
||||||
|
child: _ModeChip(
|
||||||
|
label: 'Bemérés',
|
||||||
|
icon: Icons.gps_fixed,
|
||||||
|
selected: mode == MapSurveyMode.measure,
|
||||||
|
onTap: () => controller.switchMode?.call(MapSurveyMode.measure),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
|
||||||
|
// Kitűzés chip
|
||||||
|
Expanded(
|
||||||
|
child: _ModeChip(
|
||||||
|
label: 'Kitűzés',
|
||||||
|
icon: Icons.flag_outlined,
|
||||||
|
selected: mode == MapSurveyMode.stakeout,
|
||||||
|
onTap: () => controller.switchMode?.call(MapSurveyMode.stakeout),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
|
||||||
|
// Mentés gomb — bemérés módban kis gomb
|
||||||
|
if (mode == MapSurveyMode.measure)
|
||||||
|
GestureDetector(
|
||||||
|
onTap: onSave,
|
||||||
|
child: Obx(() {
|
||||||
|
final hasfix = GnssService.to.gpsQuality.value > 0;
|
||||||
|
return Container(
|
||||||
|
width: 44,
|
||||||
|
height: 32,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: hasfix ? Colors.blue.shade700 : Colors.grey.shade800,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.location_on,
|
||||||
|
color: hasfix ? Colors.white : Colors.grey,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Módváltó chip ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class _ModeChip extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
final IconData icon;
|
||||||
|
final bool selected;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
const _ModeChip({
|
||||||
|
required this.label,
|
||||||
|
required this.icon,
|
||||||
|
required this.selected,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
height: 32,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: selected
|
||||||
|
? Colors.blue.withOpacity(0.25)
|
||||||
|
: Colors.white.withOpacity(0.06),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(
|
||||||
|
color: selected
|
||||||
|
? Colors.blue.shade300.withOpacity(0.6)
|
||||||
|
: Colors.white.withOpacity(0.12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
size: 14,
|
||||||
|
color: selected ? Colors.blue.shade300 : Colors.white38,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 5),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: selected ? Colors.blue.shade300 : Colors.white38,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Kitűzési tartalom ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class _StakeoutContent extends StatelessWidget {
|
||||||
|
final dynamic controller;
|
||||||
|
final VoidCallback onSave;
|
||||||
|
|
||||||
|
const _StakeoutContent({
|
||||||
|
required this.controller,
|
||||||
|
required this.onSave,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Obx(() {
|
||||||
|
final dy = (controller.deltaY as double?) ?? 0.0;
|
||||||
|
final dx = (controller.deltaX as double?) ?? 0.0;
|
||||||
|
final dist = (controller.distanceToTarget as double?) ?? 0.0;
|
||||||
|
final onTarget = (controller.isOnTarget as bool?) ?? false;
|
||||||
|
final name = (controller.targetName as RxString?) ?? '';
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(10, 6, 10, 6),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// ΔY / ΔX / Távolság
|
||||||
|
Row(children: [
|
||||||
|
_DeltaCell('ΔY', dy),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
_DeltaCell('ΔX', dx),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
_DeltaCell('Táv', dist, alwaysPositive: true),
|
||||||
|
]),
|
||||||
|
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
|
||||||
|
// Célpont státusz
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: onTarget
|
||||||
|
? Colors.green.withOpacity(0.12)
|
||||||
|
: Colors.orange.withOpacity(0.10),
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
border: Border.all(
|
||||||
|
color: onTarget
|
||||||
|
? Colors.green.withOpacity(0.4)
|
||||||
|
: Colors.orange.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
onTarget
|
||||||
|
? '✓ Célponton — rögzíthető'
|
||||||
|
: '$name · ${dist.toStringAsFixed(3)} m a céltól',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: onTarget ? Colors.greenAccent : Colors.orange,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
|
||||||
|
// Nagy mentés gomb kitűzés módban
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 36,
|
||||||
|
child: FilledButton.icon(
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor:
|
||||||
|
onTarget ? Colors.green.shade700 : Colors.blue.shade700,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8)),
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.location_on, size: 18),
|
||||||
|
label: const Text('Pont rögzítése',
|
||||||
|
style: TextStyle(fontSize: 13)),
|
||||||
|
onPressed: onSave,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ΔY / ΔX / Távolság cella ─────────────────────────────────────────
|
||||||
|
|
||||||
|
class _DeltaCell extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
final double value;
|
||||||
|
final bool alwaysPositive;
|
||||||
|
|
||||||
|
const _DeltaCell(this.label, this.value, {this.alwaysPositive = false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final display = alwaysPositive ? value.abs() : value;
|
||||||
|
final prefix = (!alwaysPositive && value > 0) ? '+' : '';
|
||||||
|
final color = value.abs() < 0.05
|
||||||
|
? Colors.greenAccent
|
||||||
|
: value.abs() < 0.5
|
||||||
|
? Colors.orange
|
||||||
|
: Colors.red;
|
||||||
|
|
||||||
|
return Expanded(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.05),
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 9,
|
||||||
|
color: Colors.white.withOpacity(0.4),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'$prefix${display.toStringAsFixed(3)} m',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: color,
|
||||||
|
fontFeatures: const [FontFeature.tabularFigures()],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user