Files
dmc/app/lib/ui/providers/current_mission_provider.dart
T
Constantin LeueandClaude Sonnet 5 6dcb765b80 Add waypoint route line, snap-to-edit, and halo action menu
Full reticle interaction state machine (idle/snapped/editing), matching
the HTML prototype's mode variable:

- idle: reticle shows "+", tap drops a new waypoint at the map center.
- snapped: after any pan ends, if the map center lands within the
  reticle's on-screen radius (converted to meters at the current
  zoom/latitude) of an existing waypoint, the view auto-recenters onto it
  and the reticle switches to a pencil icon. Tap to enter editing.
- editing: reticle shows a checkmark; panning the map now live-updates the
  target waypoint's position (drag-to-reposition) instead of moving the
  camera "away" from it, and the HaloMenu radial menu appears.

HaloMenu: three ring-wedge hit regions (custom ClipPath/CustomClipper,
angles/radii taken from the prototype's buildRingWedgePath) for
Loiter/Landing/Remove, with Material icons standing in for the
demonstrator's hand-drawn SVGs. Loiter/Landing toggle the waypoint's
action and drop back to snapped; Remove deletes it and returns to idle.

MissionMap now draws a straight white polyline connecting all waypoints
in order (the physically-grounded straight-line + fillet-arc geometry
from doc 3.7/4.6 is still open - this is a straight-line placeholder) plus
a dashed rubber-band preview from the last waypoint to the live map center
while idle.

Note on porting from Leaflet: flutter_map's MapController.move() emits
MapEventMove, not MapEventMoveEnd, so (unlike the prototype's panTo()) the
auto-recenter-on-snap doesn't re-trigger snap detection - no "autoPanning"
guard flag was needed.

Verified with flutter analyze, flutter test (three new tests: halo menu
open/close, remove, loiter toggle - the wedge tap tests had to target the
icon's exact center via tapAt(), since a wedge's bounding-box center falls
outside its actual donut-segment hit area), and a full manual pass on the
Pixel_10a emulator: idle -> add -> snap (auto-recenter confirmed) -> edit
-> drag-reposition (confirmed via re-snap after further panning showing
the moved position, not the original one) -> halo remove -> back to idle,
plus the connecting line rendering correctly between two waypoints set
far apart.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-27 22:56:06 +02:00

53 lines
1.6 KiB
Dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../domain/waypoint/flat_waypoint_list.dart';
/// Haelt die Wegpunktliste der aktuell in Bearbeitung befindlichen Mission
/// (Architektur-Doku 3.2/3.6). Reaktiver Einzelwert im Sinne von 4.16 -
/// daher Riverpod statt Bloc; die App-Mode State Machine bleibt in
/// AppModeCubit.
class CurrentMissionNotifier extends Notifier<List<Waypoint>> {
@override
List<Waypoint> build() => [];
void addWaypoint(Waypoint waypoint) {
state = [...state, waypoint];
}
/// Aktualisiert die Position eines Wegpunkts (Drag-to-Reposition im
/// Editier-Modus: Kartenverschieben bewegt den Zielpunkt live mit).
void moveWaypoint(int index, double lat, double lon) {
state = [
for (var i = 0; i < state.length; i++)
if (i == index) state[i].copyWith(lat: lat, lon: lon) else state[i],
];
}
/// Schaltet die Execute-Aktion eines Wegpunkts um (Halo-Radialmenue:
/// erneutes Antippen derselben Aktion setzt sie zurueck auf "none").
void toggleAction(int index, WaypointAction action) {
state = [
for (var i = 0; i < state.length; i++)
if (i == index)
state[i].copyWith(
action:
state[i].action == action ? WaypointAction.none : action,
)
else
state[i],
];
}
void removeAt(int index) {
state = [
for (var i = 0; i < state.length; i++)
if (i != index) state[i],
];
}
}
final currentMissionProvider =
NotifierProvider<CurrentMissionNotifier, List<Waypoint>>(
CurrentMissionNotifier.new,
);