Location search starts a new mission; shrink and center header bar

Ortssuche verschob bisher nur die Karte - der HTML-Demonstrator beginnt
bei einer Suche immer eine neue, nach dem gefundenen Ort benannte
Mission (setMissionFromPlace(), aufgerufen aus doSearch()). Vorherige
Aenderungen gehen dabei nicht verloren, da flushPendingAutosave()
(hier: CurrentMissionMetaNotifier._flushNow()) zuerst noch ausstehende
Autosaves schreibt.

Die Nominatim-Suche wurde dafuer aus MapSearchControls in einen
eigenstaendigen NominatimService extrahiert (analog WindService):
injectable http.Client, damit sich die Suche in Tests ohne echten
Netzwerkzugriff ueberschreiben laesst. CurrentMissionMetaNotifier bekam
dafuer startNewFromPlace() (startNew() intern darauf umgebaut, um
Duplikation zu vermeiden).

Kopfleiste von 86%/64% (Plan-/Fly-Modus) auf einheitlich 70%
Bildschirmbreite verkleinert - zentriert war sie durch Align(topCenter)
+ FractionallySizedBox bereits strukturell korrekt, wirkte bei der
vollen Breite aber unausgewogen.

Verifiziert: flutter analyze (0 issues), flutter test (64/64, davon 4
neue NominatimService-Tests und 1 neuer Widget-Test fuer den
Missions-Reset bei Ortssuche), manuell auf Pixel_10a-Emulator - Suche
nach "Rotterdam" setzt Fusszeile auf "Mission: Rotterdam" mit 0
Wegpunkten trotz zuvor bestehender Mission mit Wegpunkten.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Constantin Leue
2026-07-28 15:22:36 +02:00
co-authored by Claude Sonnet 5
parent aaec7f0f72
commit 926d805156
7 changed files with 234 additions and 21 deletions
@@ -0,0 +1,71 @@
import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:dmc_app/services/geocoding/nominatim_service.dart';
http.Response _json(Object body) =>
http.Response(jsonEncode(body), 200, headers: {
'content-type': 'application/json',
});
void main() {
group('NominatimService.search', () {
test('bevorzugt city aus address gegenueber display_name', () async {
final client = MockClient((request) async {
expect(request.url.queryParameters['q'], 'Amsterdam');
return _json([
{
'lat': '52.37403',
'lon': '4.88969',
'display_name': 'Amsterdam, Noord-Holland, Netherlands',
'address': {'city': 'Amsterdam', 'county': 'Noord-Holland'},
},
]);
});
final service = NominatimService(client: client);
final result = await service.search('Amsterdam');
expect(result, isNotNull);
expect(result!.label, 'Amsterdam');
expect(result.lat, closeTo(52.37403, 0.00001));
expect(result.lon, closeTo(4.88969, 0.00001));
});
test('faellt ohne strukturierte Adresse auf display_name zurueck',
() async {
final client = MockClient((request) async {
return _json([
{
'lat': '1.0',
'lon': '2.0',
'display_name': 'Somewhere Remote, Some Region',
'address': <String, dynamic>{},
},
]);
});
final service = NominatimService(client: client);
final result = await service.search('remote place');
expect(result!.label, 'Somewhere Remote');
});
test('liefert null bei leeren Ergebnissen', () async {
final client = MockClient((request) async => _json([]));
final service = NominatimService(client: client);
expect(await service.search('nowhere'), isNull);
});
test('liefert null bei fehlgeschlagenem Request', () async {
final client = MockClient((request) async => http.Response('', 500));
final service = NominatimService(client: client);
expect(await service.search('anywhere'), isNull);
});
});
}