Add location search, home/fit map buttons, and slim OSM attribution
- TopNavBar: Nominatim geocoding search field plus Home (center on first waypoint) and Fit (zoom to mission bounds) buttons (HTML prototype: #searchWrap/#homeBtn/#fitBtn). Both show a SnackBar instead of doing nothing when there are no waypoints yet. - Renders as its own bar just below TopModeBar rather than fused into the same pill: TopModeBar lives in AppShell, a layer above PlanScreen, which owns the MapController these actions actually need. Fusing them would need lifting the MapController to shared state - reasonable follow-up if pixel-fidelity with the prototype's single bar matters later, but not necessary for the functionality itself. - Replaced SimpleAttributionWidget with a compact custom attribution: OSM's tile usage policy requires visible attribution to stay, so instead of removing it (as first asked) I shrank it and dropped the "flutter_map |" prefix per the user's follow-up choice, so it reads cleanly against the now-opaque bottom bar instead of looking like a second banner. Added 5 tests (nav bar renders, home/fit SnackBars with no waypoints, empty search is a no-op). All 26 tests and flutter analyze pass. Verified on the Pixel_10a emulator: searched "Berlin" and confirmed the map flew there, dropped a waypoint, panned far away, and confirmed Home re-centers and snaps onto it exactly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
3091476771
commit
dc8e6f9d6e
@@ -11,6 +11,7 @@ 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/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';
|
||||
|
||||
@@ -309,4 +310,46 @@ void main() {
|
||||
final waypoints = container.read(currentMissionProvider);
|
||||
expect(waypoints.single.speedMs, greaterThan(15));
|
||||
});
|
||||
|
||||
testWidgets('TopNavBar zeigt Suchfeld und Home-/Fit-Buttons',
|
||||
(tester) async {
|
||||
await _pumpWithContainer(tester);
|
||||
|
||||
expect(find.byType(TopNavBar), findsOneWidget);
|
||||
expect(find.text('Search location'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.home), findsOneWidget);
|
||||
expect(find.byIcon(Icons.crop_free), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Home-Button ohne Wegpunkte zeigt Hinweis statt Absturz',
|
||||
(tester) async {
|
||||
await _pumpWithContainer(tester);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.home));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('No waypoints yet'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Fit-Button ohne Wegpunkte zeigt Hinweis statt Absturz',
|
||||
(tester) async {
|
||||
await _pumpWithContainer(tester);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.crop_free));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('No waypoints to fit'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Leere Suche loest keinen Netzwerkaufruf/Fehler aus',
|
||||
(tester) async {
|
||||
await _pumpWithContainer(tester);
|
||||
|
||||
await tester.enterText(find.byType(TextField), '');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.search);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Search failed'), findsNothing);
|
||||
expect(find.text('Location not found'), findsNothing);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user