live mission details in footer implemented

This commit is contained in:
Constantin Leue
2026-08-04 09:48:01 +02:00
parent 35351140ae
commit a66c199b37
17 changed files with 295 additions and 17 deletions
+7 -1
View File
@@ -6,6 +6,7 @@ import '../../transport/link_transport.dart';
import '../../transport/mock/mock_flight_controller_link.dart';
import '../../transport/msp/msp_flight_controller_link.dart';
import 'connection_settings_provider.dart';
import 'current_mission_provider.dart';
import 'wifi_connection_provider.dart';
/// FlightControllerLink fuer den Fly-Modus (Doku 3.1): bei WLAN als
@@ -41,7 +42,12 @@ final flightControllerLinkProvider = Provider<FlightControllerLink>((ref) {
final connectionType = ref.watch(connectionSettingsProvider).value?.connectionType;
final FlightControllerLink link = connectionType == ConnectionType.wifi
? MspFlightControllerLink(transport: ref.watch(wifiTransportProvider))
: MockFlightControllerLink();
// ref.read() statt watch() - der Mock soll nur einmal bei seiner
// Erzeugung am ersten Wegpunkt der GERADE aktuellen Mission
// verankert werden (Doku: "Mock-Daten fuer die Live-Details-Ansicht
// nutzbar machen"), nicht bei jeder spaeteren Missionsaenderung
// neu erzeugt werden (das wuerde Timer/Drift/Akkustand zuruecksetzen).
: MockFlightControllerLink(initialWaypoints: ref.read(currentMissionProvider));
ref.onDispose(link.disconnect);
return link;
});
+14
View File
@@ -226,6 +226,19 @@ class _FlyScreenState extends ConsumerState<FlyScreen> {
];
}
// Live-Missionsstatus fuer die Details-Pille (Doku: "aktiver Wegpunkt
// 3/23, Distanz und Zeit zum naechsten Wegpunkt" statt der statischen
// Plan-Werte) - siehe FlyMissionStatus-Klassendoku dazu, warum das aus
// Position+Wegpunktkoordinaten berechnet wird statt direkt von MSP
// abgefragt.
final flyMissionStatus = computeFlyMissionStatus(
waypoints: waypoints,
activeWaypointIndex: activeWpIndex,
currentLat: telemetry != null && telemetry.hasFix ? telemetry.lat : null,
currentLon: telemetry != null && telemetry.hasFix ? telemetry.lon : null,
currentSpeedMs: telemetry?.speedMs,
);
return Stack(
children: [
MissionMap(
@@ -273,6 +286,7 @@ class _FlyScreenState extends ConsumerState<FlyScreen> {
activeIndex: -1,
totalDistanceM: missionStats.distanceM,
totalDurationSec: missionStats.durationSec,
flyMissionStatus: flyMissionStatus,
warnings: warnings,
onDetailsTap: _openWaypointList,
onWarningsTap: () => _openWarningsPanel(warnings),
@@ -278,7 +278,9 @@ class _TelemetryFields extends StatelessWidget {
_field('Link quality', '${t.linkQuality}%'),
_field('Flight mode', t.flightMode),
if (t.activeWaypointIndex != null)
_field('Active WP', '${t.activeWaypointIndex}'),
// +1: activeWaypointIndex ist 0-basiert (Array-Index), fuer
// Menschen zaehlt man Wegpunkte ab 1 (siehe TelemetryFrame-Doku).
_field('Active WP', '${t.activeWaypointIndex! + 1}'),
],
);
}
+29 -3
View File
@@ -3,6 +3,7 @@ import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../domain/mission/mission_stats.dart';
import '../../domain/mission/mission_warnings.dart';
import '../providers/active_drone_profile_provider.dart';
import '../providers/altitude_profile_provider.dart';
@@ -25,6 +26,7 @@ class BottomStatsBar extends ConsumerWidget {
required this.waypointCount,
required this.totalDistanceM,
required this.totalDurationSec,
this.flyMissionStatus,
required this.onTap,
required this.onSettingsTap,
this.warnings = const [],
@@ -34,6 +36,10 @@ class BottomStatsBar extends ConsumerWidget {
final int waypointCount;
final double totalDistanceM;
final double totalDurationSec;
/// Siehe MissionFooterBar-Doku - nur im Fly-Modus gesetzt, ersetzt dann
/// die statischen Felder in der Details-Pille.
final FlyMissionStatus? flyMissionStatus;
final VoidCallback onTap;
/// Oeffnet das Settings-Menue (runder Zahnrad-Knopf rechts der
@@ -147,6 +153,26 @@ class BottomStatsBar extends ConsumerWidget {
}
Widget _detailsPill() {
final flyStatus = flyMissionStatus;
// Im Fly-Modus (flyStatus != null) ersetzen aktiver Wegpunkt sowie
// Distanz/Zeit dorthin die statischen Plan-Werte (Doku: "Live-Status
// der Mission in der Details-Pille, genau wie vorher aber mit aktivem
// Wegpunkt statt Gesamtzahl, Distanz/Zeit zum naechsten Wegpunkt statt
// Gesamtdistanz/-dauer").
final waypointText = flyStatus != null
? '${flyStatus.activeWaypointNumber}/${flyStatus.waypointCount}'
: '$waypointCount WP';
final distanceText = flyStatus != null
? (flyStatus.distanceToActiveWaypointM != null
? '${flyStatus.distanceToActiveWaypointM!.round()} m'
: '—')
: '${totalDistanceM.round()} m';
final durationText = flyStatus != null
? (flyStatus.etaToActiveWaypointSec != null
? _fmtDuration(flyStatus.etaToActiveWaypointSec!)
: '—')
: _fmtDuration(totalDurationSec);
return GestureDetector(
onTap: onTap,
child: Container(
@@ -169,17 +195,17 @@ class BottomStatsBar extends ConsumerWidget {
const SizedBox(width: 10),
const Icon(Icons.flag, size: 14, color: Colors.white),
const SizedBox(width: 4),
Text('$waypointCount WP',
Text(waypointText,
style: const TextStyle(color: Colors.white, fontSize: 14)),
const SizedBox(width: 14),
const Icon(Icons.straighten, size: 14, color: Colors.white),
const SizedBox(width: 4),
Text('${totalDistanceM.round()} m',
Text(distanceText,
style: const TextStyle(color: Colors.white, fontSize: 14)),
const SizedBox(width: 14),
const Icon(Icons.access_time, size: 14, color: Colors.white),
const SizedBox(width: 4),
Text(_fmtDuration(totalDurationSec),
Text(durationText,
style: const TextStyle(color: Colors.white, fontSize: 14)),
],
),
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../domain/mission/mission_stats.dart';
import '../../domain/mission/mission_warnings.dart';
import '../../domain/waypoint/flat_waypoint_list.dart';
import '../providers/altitude_profile_provider.dart';
@@ -20,6 +21,7 @@ class MissionFooterBar extends ConsumerWidget {
required this.activeIndex,
required this.totalDistanceM,
required this.totalDurationSec,
this.flyMissionStatus,
required this.warnings,
required this.onDetailsTap,
required this.onWarningsTap,
@@ -32,6 +34,12 @@ class MissionFooterBar extends ConsumerWidget {
final int activeIndex;
final double totalDistanceM;
final double totalDurationSec;
/// Nur im Fly-Modus gesetzt (Doku: "Live-Status der Mission in der
/// Details-Pille") - ersetzt dort die statischen Plan-Werte
/// (Wegpunktzahl/Gesamtdistanz/-dauer) durch aktiven Wegpunkt sowie
/// Distanz/Zeit dorthin. Bleibt im Plan-Modus null.
final FlyMissionStatus? flyMissionStatus;
final List<MissionWarning> warnings;
final VoidCallback onDetailsTap;
final VoidCallback onWarningsTap;
@@ -75,6 +83,7 @@ class MissionFooterBar extends ConsumerWidget {
waypointCount: waypoints.length,
totalDistanceM: totalDistanceM,
totalDurationSec: totalDurationSec,
flyMissionStatus: flyMissionStatus,
onTap: onDetailsTap,
warnings: warnings,
onWarningsTap: onWarningsTap,