Auto-scroll waypoint list to active row; fix speed chip step size

Wegpunktliste: der aktive Wegpunkt (per Halo-Menue "Edit" ausgewaehlt)
war bei laengeren Missionen ausserhalb des sichtbaren Bereichs, da die
Liste beim Oeffnen immer oben startete. WaypointListPanel bekommt jetzt
einen ScrollController mit fester Zeilenhoehe (itemExtent, damit die
Zielposition ohne Layout-Messung berechnet werden kann) und scrollt per
post-frame-Callback zur aktiven Zeile - sowohl beim ersten Oeffnen als
auch beim Zurueckwechseln vom Altitude-/Speed-Tab auf List.

WaypointChip (der Swipe-Wertechip in den Listenzeilen) hatte eine fest
einprogrammierte Schrittgroesse von 10 fuer alle Felder (Alt/Speed/
Catch) - fuer Speed bei einer Spanne von nur 13-25 m/s viel zu grob,
ein einzelner Drag-Schritt sprang schon fast durch den gesamten
gueltigen Bereich. Schrittgroesse ist jetzt ein expliziter Parameter;
Speed nutzt DefaultDroneProfile.speedStep (1), Altitude weiterhin
DefaultDroneProfile.altStep (10), Catch-Radius weiterhin 10 (keine
Beschwerde hierzu, Wert unveraendert). Der Alt-/Speed-Drehrad und die
Vollbild-Charts nutzten diese Profile bereits korrekt - der Row-Chip
war die einzige Stelle mit hartcodiertem Wert.

Verifiziert: flutter analyze (0 issues), flutter test (24/24, zwei neue
Tests fuer Auto-Scroll und Speed-Schrittgroesse), manuell auf
Pixel_10a-Emulator mit 7 Wegpunkten - Liste oeffnet direkt bei
Wegpunkt 7, Speed-Chip-Drag aendert 15 m/s in kleinen Schritten (18)
statt in Zehnerspruengen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Constantin Leue
2026-07-28 10:33:25 +02:00
co-authored by Claude Sonnet 5
parent 13da935f99
commit 7a26002a63
3 changed files with 123 additions and 4 deletions
+73
View File
@@ -267,6 +267,37 @@ void main() {
expect(waypoints.single.altitudeM, greaterThan(60));
});
testWidgets(
'Speed-Chip in der Liste aendert die Geschwindigkeit nur in Schritten von 1',
(tester) async {
final container = await _pumpWithContainer(tester);
await tester.tap(find.byType(ReticleButton));
await tester.pump();
await tester.tap(find.text('Details:'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
await tester.tap(find.descendant(
of: find.byType(WaypointListPanel),
matching: find.byIcon(Icons.edit),
));
await tester.pump();
// Speed-Chip ist der zweite der drei Chips (Alt/Speed/Catch). Ein Drag
// von nur 3 gemeinten Schritten (42px, abzueglich Touch-Slop real
// weniger) darf bei Schrittgroesse 1 hoechstens um wenige m/s aendern -
// mit der alten Schrittgroesse 10 waere schon ein einziger erkannter
// Schritt (+10) ausserhalb dieser Spanne.
final startSpeed = container.read(currentMissionProvider).single.speedMs;
await tester.drag(find.byType(WaypointChip).at(1), const Offset(0, -42));
await tester.pump(const Duration(milliseconds: 200));
final speed = container.read(currentMissionProvider).single.speedMs;
expect(speed, greaterThan(startSpeed));
expect(speed, lessThan(startSpeed + 4));
});
testWidgets(
'Altitude-Tab aendert die Hoehe per Drag auf dem Chart-Punkt',
(tester) async {
@@ -397,4 +428,46 @@ void main() {
expect(camera.center.longitude, closeTo(movedTo.longitude, 0.0001));
expect(camera.zoom, 14);
});
testWidgets(
'Wegpunktliste scrollt beim Oeffnen automatisch zum aktiven Wegpunkt',
(tester) async {
final container = ProviderContainer();
addTearDown(container.dispose);
final notifier = container.read(currentMissionProvider.notifier);
for (var i = 0; i < 30; i++) {
notifier.addWaypoint(Waypoint(
lat: 48.2 + i * 0.001,
lon: 16.3,
altitudeM: 60,
speedMs: 15,
catchRadiusM: 60,
));
}
await tester.pumpWidget(
UncontrolledProviderScope(
container: container,
child: MaterialApp(
home: WaypointListPanel(
activeIndex: 29,
onClose: () {},
onCenterOnWaypoint: (_) {},
onDelete: (_) {},
onAltitudeChanged: (_, _) {},
onSpeedChanged: (_, _) {},
onCatchRadiusChanged: (_, _) {},
onActionChanged: (_, _) {},
),
),
),
);
await tester.pump();
// Auto-Scroll laeuft als post-frame-Callback mit 300ms Animation.
await tester.pump(const Duration(milliseconds: 350));
final listView = tester.widget<ListView>(find.byType(ListView));
expect(listView.controller!.offset, greaterThan(0));
});
}