Auto-fetch wind, drop manual fetch/validate buttons, fix it in fly mode

Redesigns the waypoint list panel to match the drone-status/missions
panels: no title bar, close button floats over the tab row, tabs get
more vertical padding.

Removes the "Fetch wind" and "Validate" header buttons along with the
now-fully-orphaned WindValidatePanel screen and its WindService
support code (fetchValidation, WindValidationResult and friends) -
nothing else referenced them.

Wind is now fetched automatically instead: HeaderWindNotifier gains
ensurePerWaypointWind(), called when the waypoint list panel opens and
when the header pill's per-waypoint toggle switches on. A lat/lon
signature of the waypoint list tracks whether the fetched data is
still current, so a ref.listen(currentMissionProvider) in the same
notifier refetches only when the list itself changes (position added/
removed/moved) - not on every altitude/speed edit. Waypoints that
already carry wind data (e.g. loaded from a saved mission) short-
circuit the check instead of re-fetching pointlessly.

Also wires per-waypoint wind markers into the fly-mode map, which
never had them (plan_screen.dart built windMarkers; fly_screen.dart
just never did) - the reported "doesn't work in fly mode" bug.
windMarkerWidth() moved from a plan_screen.dart-private method to
wind_marker_pill.dart so both screens can share it.

Test containers in widget_test.dart now override windServiceProvider
with a MockClient so the new auto-fetch trigger can't reach the real
network during tests.
This commit is contained in:
Constantin Leue
2026-08-09 22:26:23 +02:00
parent 6b7977eca1
commit eca1c647b3
10 changed files with 368 additions and 570 deletions
+15
View File
@@ -18,6 +18,7 @@ import 'package:dmc_app/services/geocoding/nominatim_service.dart';
import 'package:dmc_app/services/sharing/drone_share_codec.dart';
import 'package:dmc_app/services/sharing/mission_share_codec.dart';
import 'package:dmc_app/services/sharing/sharing_service.dart';
import 'package:dmc_app/services/wind/wind_service.dart';
import 'package:dmc_app/ui/app_shell.dart';
import 'package:dmc_app/ui/screens/fly/fly_screen.dart';
import 'package:dmc_app/ui/providers/active_drone_profile_provider.dart';
@@ -28,6 +29,7 @@ import 'package:dmc_app/ui/providers/mission_meta_provider.dart';
import 'package:dmc_app/ui/providers/sharing_provider.dart';
import 'package:dmc_app/ui/providers/system_message_log_provider.dart';
import 'package:dmc_app/ui/providers/telemetry_provider.dart';
import 'package:dmc_app/ui/providers/wind_provider.dart';
import 'package:dmc_app/transport/mock/mock_flight_controller_link.dart';
import 'package:dmc_app/ui/widgets/drone_follow_icon.dart';
import 'package:dmc_app/ui/widgets/drone_status_messages_panel.dart';
@@ -89,6 +91,16 @@ ProviderContainer _testContainer() => ProviderContainer(
ref.onDispose(link.disconnect);
return link;
}),
// Ohne diesen Override wuerde headerWindProvider.ensurePerWaypointWind()
// (seit dem automatischen Windabruf beim Oeffnen der Wegpunktliste/
// Aktivieren der Kopfleisten-Pille) einen echten Netzwerkrequest
// ausloesen, sobald ein Test Wegpunkte ohne bereits gesetzte
// Winddaten anlegt - MockClient antwortet stattdessen sofort mit
// einem Fehlschlag, den WindService._fetchJson ohnehin schon
// abfaengt (liefert dann einfach kein Windergebnis).
windServiceProvider.overrideWithValue(
WindService(client: MockClient((request) async => http.Response('', 500))),
),
],
);
@@ -108,6 +120,9 @@ Widget _wrap(Widget child) => ProviderScope(
ref.onDispose(link.disconnect);
return link;
}),
windServiceProvider.overrideWithValue(
WindService(client: MockClient((request) async => http.Response('', 500))),
),
],
child: BlocProvider(
create: (_) => AppModeCubit(),