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:
co-authored by
Claude Sonnet 5
parent
13da935f99
commit
7a26002a63
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
|
||||
/// Swipe-adjustierbarer Wertechip in der Wegpunktliste (HTML-Demonstrator:
|
||||
/// .value-chip / chipFieldConfig). Vertikal ziehen aendert den Wert in
|
||||
/// festen Schritten von 10 pro 14px - nach oben erhoeht.
|
||||
/// festen Schritten von [step] pro 14px - nach oben erhoeht.
|
||||
class WaypointChip extends StatefulWidget {
|
||||
const WaypointChip({
|
||||
super.key,
|
||||
@@ -10,6 +10,7 @@ class WaypointChip extends StatefulWidget {
|
||||
required this.unit,
|
||||
required this.min,
|
||||
required this.max,
|
||||
required this.step,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@@ -17,9 +18,9 @@ class WaypointChip extends StatefulWidget {
|
||||
final String unit;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final ValueChanged<double> onChanged;
|
||||
|
||||
static const _stepSize = 10.0;
|
||||
static const _pxPerStep = 14.0;
|
||||
|
||||
@override
|
||||
@@ -41,7 +42,7 @@ class _WaypointChipState extends State<WaypointChip> {
|
||||
if (startY == null) return;
|
||||
final deltaY = startY - details.globalPosition.dy;
|
||||
final steps = (deltaY / WaypointChip._pxPerStep).round();
|
||||
var val = _dragStartValue + steps * WaypointChip._stepSize;
|
||||
var val = _dragStartValue + steps * widget.step;
|
||||
val = val.clamp(widget.min, widget.max);
|
||||
if (val != widget.value) {
|
||||
widget.onChanged(val);
|
||||
|
||||
@@ -51,6 +51,7 @@ class WaypointListPanel extends ConsumerStatefulWidget {
|
||||
class _WaypointListPanelState extends ConsumerState<WaypointListPanel> {
|
||||
int _rowEditIndex = -1;
|
||||
_PanelTab _tab = _PanelTab.list;
|
||||
final _listScrollController = ScrollController();
|
||||
|
||||
static const _headerStyle = TextStyle(
|
||||
color: Colors.white60,
|
||||
@@ -59,6 +60,40 @@ class _WaypointListPanelState extends ConsumerState<WaypointListPanel> {
|
||||
letterSpacing: 0.5,
|
||||
);
|
||||
|
||||
// Feste Zeilenhoehe, damit die Scrollposition des aktiven Wegpunkts ohne
|
||||
// Layout-Messung berechnet werden kann (siehe _scrollToActive).
|
||||
static const _rowExtent = 60.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToActive());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_listScrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// Zentriert die Liste auf den aktuell aktiven Wegpunkt (Doku 3.6: das
|
||||
/// Panel oeffnet sich beim Editieren eines Wegpunkts ueber das
|
||||
/// Halo-Menue - ohne Auto-Scroll waere er bei laengeren Missionen nicht
|
||||
/// sichtbar).
|
||||
void _scrollToActive() {
|
||||
if (!mounted || widget.activeIndex < 0) return;
|
||||
if (!_listScrollController.hasClients) return;
|
||||
final position = _listScrollController.position;
|
||||
final target = widget.activeIndex * _rowExtent -
|
||||
position.viewportDimension / 2 +
|
||||
_rowExtent / 2;
|
||||
_listScrollController.animateTo(
|
||||
target.clamp(0.0, position.maxScrollExtent),
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final waypoints = ref.watch(currentMissionProvider);
|
||||
@@ -140,7 +175,12 @@ class _WaypointListPanelState extends ConsumerState<WaypointListPanel> {
|
||||
final active = _tab == tab;
|
||||
return Expanded(
|
||||
child: InkWell(
|
||||
onTap: () => setState(() => _tab = tab),
|
||||
onTap: () {
|
||||
setState(() => _tab = tab);
|
||||
if (tab == _PanelTab.list) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToActive());
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
@@ -220,7 +260,9 @@ class _WaypointListPanelState extends ConsumerState<WaypointListPanel> {
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
controller: _listScrollController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
itemExtent: _rowExtent,
|
||||
itemCount: waypoints.length,
|
||||
itemBuilder: (context, index) =>
|
||||
_row(waypoints[index], index, routeGeometry),
|
||||
@@ -273,6 +315,7 @@ class _WaypointListPanelState extends ConsumerState<WaypointListPanel> {
|
||||
unit: ' m',
|
||||
min: DefaultDroneProfile.altMin,
|
||||
max: DefaultDroneProfile.altMax,
|
||||
step: DefaultDroneProfile.altStep,
|
||||
onChanged: (v) => widget.onAltitudeChanged(index, v),
|
||||
)
|
||||
: Text('${wp.altitudeM.round()} m',
|
||||
@@ -285,6 +328,7 @@ class _WaypointListPanelState extends ConsumerState<WaypointListPanel> {
|
||||
unit: ' m/s',
|
||||
min: DefaultDroneProfile.speedMin,
|
||||
max: DefaultDroneProfile.speedMax,
|
||||
step: DefaultDroneProfile.speedStep,
|
||||
onChanged: (v) => widget.onSpeedChanged(index, v),
|
||||
)
|
||||
: Text('${wp.speedMs.round()} m/s',
|
||||
@@ -325,6 +369,7 @@ class _WaypointListPanelState extends ConsumerState<WaypointListPanel> {
|
||||
unit: ' m',
|
||||
min: 5,
|
||||
max: 500,
|
||||
step: 10,
|
||||
onChanged: (v) => widget.onCatchRadiusChanged(index, v),
|
||||
)
|
||||
: Text('${wp.catchRadiusM.round()} m',
|
||||
|
||||
Reference in New Issue
Block a user