Merge search/home/fit controls into the Plan/Fly header pill
Suche, Home- und Fit-Button sassen bisher in einer eigenen Leiste unter der Plan/Fly-Kopfleiste - ein reiner UI-Kompromiss, weil die Karte (inkl. MapController) in PlanScreen lebte und von der aeusseren Kopfleiste aus nicht erreichbar war. Der HTML-Demonstrator zeigt Suche/Home/Fit dagegen als Mittelteil derselben Leiste wie die Modus-Buttons (#searchWrap/#homeBtn/#fitBtn in #topBarMiddle) - das war also keine Stilfrage, sondern eine Datenfluss-Frage. Loesung: MapController aus einem PlanScreen-privaten Feld in einen app-lebenslangen mapControllerProvider (Riverpod) gehoben. Damit kann die neue MapSearchControls (ersetzt TopNavBar) direkt in TopModeBar eingebettet werden und lebt architektonisch dort, wo sie hingehoert: bei der Kartensteuerung, nicht als Kind von PlanScreen. Als Nebeneffekt - tatsaechlich der wichtigere Punkt - bleibt die Kamera-Position/Zoom jetzt auch beim Wechsel Plan -> Fly -> Plan erhalten. Verifiziert per flutter_map-Quellcode (FlutterMap haengt einen extern uebergebenen Controller beim Neu-Mounten nicht an und disposed ihn nicht) sowie per neuem Widget-Test, der den Cubit direkt durch das Ready-to-Fly-Gate schickt (der Fly-Modus ist ueber die UI aktuell nicht erreichbar, da Upload/Verify noch nicht implementiert ist) und die Kamera vor/nach dem Wechsel vergleicht. Zusaetzlich: _onMapEvent unterscheidet jetzt per MapEventMove.source, ob eine Kartenbewegung programmatisch (mapController, z.B. durch Suche/ Home/Fit) oder per Geste ausgeloest wurde, und triggert den Snap-to-Edit-Handler entsprechend nur bei Gesten bzw. sofort bei programmatischen Moves - das ersetzt den bisherigen Ansatz, an jeder Aufrufstelle manuell _handleMoveEnd() zu rufen, der nicht mehr skaliert sobald diese Aufrufstellen (wie jetzt) in einem anderen Widget liegen. Verifiziert: flutter analyze (0 issues), flutter test (22/22, inkl. neuem Kamera-Persistenz-Test), manuell auf Pixel_10a-Emulator (Release- Build) - Suche/Home/Fit erscheinen fusioniert in der gruenen Pille, Kartenverschiebung bleibt nach Wechsel in den (durch das Gate weiterhin blockierten) Fly-Modus sichtbar unveraendert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
dc8e6f9d6e
commit
13da935f99
@@ -10,10 +10,12 @@ import 'package:dmc_app/ui/app_shell.dart';
|
||||
import 'package:dmc_app/ui/providers/current_mission_provider.dart';
|
||||
import 'package:dmc_app/ui/widgets/full_value_chart.dart';
|
||||
import 'package:dmc_app/ui/widgets/halo_menu.dart';
|
||||
import 'package:dmc_app/ui/widgets/map_search_controls.dart';
|
||||
import 'package:dmc_app/ui/providers/map_controller_provider.dart';
|
||||
import 'package:dmc_app/ui/widgets/reticle_button.dart';
|
||||
import 'package:dmc_app/ui/widgets/top_nav_bar.dart';
|
||||
import 'package:dmc_app/ui/widgets/waypoint_chip.dart';
|
||||
import 'package:dmc_app/ui/widgets/waypoint_list_panel.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
|
||||
Widget _wrap(Widget child) => ProviderScope(
|
||||
child: BlocProvider(
|
||||
@@ -311,11 +313,11 @@ void main() {
|
||||
expect(waypoints.single.speedMs, greaterThan(15));
|
||||
});
|
||||
|
||||
testWidgets('TopNavBar zeigt Suchfeld und Home-/Fit-Buttons',
|
||||
testWidgets('MapSearchControls zeigt Suchfeld und Home-/Fit-Buttons',
|
||||
(tester) async {
|
||||
await _pumpWithContainer(tester);
|
||||
|
||||
expect(find.byType(TopNavBar), findsOneWidget);
|
||||
expect(find.byType(MapSearchControls), findsOneWidget);
|
||||
expect(find.text('Search location'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.home), findsOneWidget);
|
||||
expect(find.byIcon(Icons.crop_free), findsOneWidget);
|
||||
@@ -352,4 +354,47 @@ void main() {
|
||||
expect(find.text('Search failed'), findsNothing);
|
||||
expect(find.text('Location not found'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Kameraposition bleibt beim Wechsel Plan -> Fly -> Plan erhalten',
|
||||
(tester) async {
|
||||
final container = ProviderContainer();
|
||||
final appModeCubit = AppModeCubit();
|
||||
addTearDown(() {
|
||||
container.dispose();
|
||||
appModeCubit.close();
|
||||
});
|
||||
|
||||
await tester.pumpWidget(
|
||||
UncontrolledProviderScope(
|
||||
container: container,
|
||||
child: BlocProvider<AppModeCubit>.value(
|
||||
value: appModeCubit,
|
||||
child: const MaterialApp(home: AppShell()),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
const movedTo = LatLng(48.2082, 16.3738);
|
||||
container.read(mapControllerProvider).move(movedTo, 14);
|
||||
await tester.pump();
|
||||
|
||||
// Ready-to-Fly-Gate technisch erfuellen, um in den Fly-Modus zu gelangen
|
||||
// (Doku 2.2/4.5) - hier nur, um die Kamera-Persistenz des geteilten
|
||||
// mapControllerProvider ueber den Moduswechsel zu pruefen.
|
||||
appModeCubit
|
||||
..missionUploadConfirmed()
|
||||
..missionVerifiedConfirmed()
|
||||
..toFly();
|
||||
await tester.pump();
|
||||
|
||||
appModeCubit.toPlan();
|
||||
await tester.pump();
|
||||
|
||||
final camera = container.read(mapControllerProvider).camera;
|
||||
expect(camera.center.latitude, closeTo(movedTo.latitude, 0.0001));
|
||||
expect(camera.center.longitude, closeTo(movedTo.longitude, 0.0001));
|
||||
expect(camera.zoom, 14);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user