live mission details in footer implemented
This commit is contained in:
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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}'),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user