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>
88 lines
2.5 KiB
Dart
88 lines
2.5 KiB
Dart
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 [step] pro 14px - nach oben erhoeht.
|
|
class WaypointChip extends StatefulWidget {
|
|
const WaypointChip({
|
|
super.key,
|
|
required this.value,
|
|
required this.unit,
|
|
required this.min,
|
|
required this.max,
|
|
required this.step,
|
|
required this.onChanged,
|
|
});
|
|
|
|
final double value;
|
|
final String unit;
|
|
final double min;
|
|
final double max;
|
|
final double step;
|
|
final ValueChanged<double> onChanged;
|
|
|
|
static const _pxPerStep = 14.0;
|
|
|
|
@override
|
|
State<WaypointChip> createState() => _WaypointChipState();
|
|
}
|
|
|
|
class _WaypointChipState extends State<WaypointChip> {
|
|
double? _dragStartY;
|
|
double _dragStartValue = 0;
|
|
bool _flash = false;
|
|
|
|
void _onDragStart(DragStartDetails details) {
|
|
_dragStartY = details.globalPosition.dy;
|
|
_dragStartValue = widget.value;
|
|
}
|
|
|
|
void _onDragUpdate(DragUpdateDetails details) {
|
|
final startY = _dragStartY;
|
|
if (startY == null) return;
|
|
final deltaY = startY - details.globalPosition.dy;
|
|
final steps = (deltaY / WaypointChip._pxPerStep).round();
|
|
var val = _dragStartValue + steps * widget.step;
|
|
val = val.clamp(widget.min, widget.max);
|
|
if (val != widget.value) {
|
|
widget.onChanged(val);
|
|
setState(() => _flash = true);
|
|
Future.delayed(const Duration(milliseconds: 120), () {
|
|
if (mounted) setState(() => _flash = false);
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GestureDetector(
|
|
onVerticalDragStart: _onDragStart,
|
|
onVerticalDragUpdate: _onDragUpdate,
|
|
onVerticalDragEnd: (_) => _dragStartY = null,
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
|
decoration: BoxDecoration(
|
|
color: _flash
|
|
? const Color(0xFF4D9DFF).withValues(alpha: 0.4)
|
|
: Colors.black.withValues(alpha: 0.85),
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(
|
|
color: _flash
|
|
? const Color(0xFF8FD0FF)
|
|
: const Color(0xFF4D9DFF).withValues(alpha: 0.7),
|
|
width: 1.5,
|
|
),
|
|
),
|
|
child: Text(
|
|
'${widget.value.round()}${widget.unit}',
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 13,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|