lib/location_screen.dartDart
import 'package:flutter/material.dart';
import 'package:apple_maps_flutter/apple_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:osm_nominatim/osm_nominatim.dart';
import 'constants.dart';
import 'core/prefs/pref_keys.dart';
class LocationScreen extends StatefulWidget {
const LocationScreen({super.key});
@override
State<LocationScreen> createState() => _LocationScreenState();
}
class _LocationScreenState extends State<LocationScreen> {
final TextEditingController _controller = TextEditingController();
AppleMapController? _mapController;
LatLng _currentLatLng = const LatLng(50.9794, 11.3297); // Weimar Default
List<Place> _suggestions = [];
bool _isSearching = false;
void _onMapCreated(AppleMapController controller) {
_mapController = controller;
}
// 1. Suche über Texteingabe
Future<void> _searchAddress(String query) async {
if (query.length < 3) {
setState(() => _suggestions = []);
return;
}
setState(() => _isSearching = true);
try {
final nominatim = Nominatim(userAgent: 'MeinWeimarRettertueteApp');
final searchResult = await nominatim.searchByName(
query: query,
limit: 5,
addressDetails: true,
);
setState(() {
_suggestions = searchResult;
_isSearching = false;
});
} catch (e) {
debugPrint("Suche Fehler: $e");
setState(() => _isSearching = false);
}
}
// 2. Aktuellen GPS Standort abfragen
Future<void> _getCurrentLocation() async {
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
}
if (permission == LocationPermission.whileInUse || permission == LocationPermission.always) {
Position position = await Geolocator.getCurrentPosition();
LatLng newPos = LatLng(position.latitude, position.longitude);
_mapController?.animateCamera(CameraUpdate.newLatLngZoom(newPos, 15));
setState(() => _currentLatLng = newPos);
}
}
// 3. Standort speichern (mit Reverse Geocoding für die Karte)
Future<void> _saveLocation(String addr, double lat, double lng, {bool performReverse = false}) async {
setState(() => _isSearching = true);
String finalAddress = addr;
if (performReverse) {
try {
final nominatim = Nominatim(userAgent: 'MeinWeimarRettertueteApp');
final reverseResult = await nominatim.reverseSearch(
lat: lat,
lon: lng,
addressDetails: true,
);
finalAddress = reverseResult.displayName;
} catch (e) {
debugPrint("Reverse Search Fehler: $e");
finalAddress = "Manuell gewählter Standort";
}
}
final prefs = await SharedPreferences.getInstance();
await prefs.setString(PrefKeys.userAddress, finalAddress);
await prefs.setDouble(PrefKeys.userLat, lat);
await prefs.setDouble(PrefKeys.userLng, lng);
if (mounted) {
setState(() => _isSearching = false);
Navigator.pop(context);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: AppConstants.fontGrey),
onPressed: () => Navigator.pop(context),
),
title: const Text("Standort festlegen",
style: TextStyle(color: AppConstants.fontGrey, fontWeight: FontWeight.bold)),
),
body: Column(
children: [
// Suchfeld
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10),
child: TextField(
controller: _controller,
onChanged: _searchAddress,
style: const TextStyle(color: AppConstants.fontGrey),
decoration: InputDecoration(
hintText: "Adresse suchen...",
prefixIcon: const Icon(Icons.search, color: AppConstants.mainColor),
suffixIcon: _isSearching && _suggestions.isEmpty
? const SizedBox(width: 20, height: 20, child: Padding(padding: EdgeInsets.all(12), child: CircularProgressIndicator(strokeWidth: 2, color: AppConstants.mainColor)))
: null,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(15)),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15), borderSide: const BorderSide(color: AppConstants.mainColor)),
),
),
),
// Suchergebnisse
if (_suggestions.isNotEmpty)
Container(
constraints: const BoxConstraints(maxHeight: 250),
margin: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15), boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 10)]),
child: ListView.builder(
shrinkWrap: true,
itemCount: _suggestions.length,
itemBuilder: (context, i) {
final s = _suggestions[i];
return ListTile(
leading: const Icon(Icons.location_on_outlined, color: AppConstants.mainColor),
title: Text(s.displayName, style: const TextStyle(fontSize: 13, color: AppConstants.fontGrey)),
onTap: () => _saveLocation(s.displayName, s.lat, s.lon),
);
},
),
),
const SizedBox(height: 10),
// Kartenbereich
Expanded(
child: Stack(
children: [
AppleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(target: _currentLatLng, zoom: 14),
myLocationEnabled: true,
myLocationButtonEnabled: false,
onCameraMove: (p) => _currentLatLng = p.target,
),
Positioned(
right: 20,
bottom: 20,
child: FloatingActionButton(
heroTag: "locBtn",
backgroundColor: Colors.white,
onPressed: _getCurrentLocation,
child: const Icon(Icons.my_location, color: AppConstants.mainColor),
),
),
const Center(
child: Padding(
padding: EdgeInsets.only(bottom: 35),
child: Icon(Icons.location_pin, size: 45, color: AppConstants.mainColor),
),
),
],
),
),
// Bestätigungs-Button
Padding(
padding: const EdgeInsets.all(20.0),
child: SizedBox(
width: double.infinity,
height: 55,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.mainColor,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
elevation: 0,
),
onPressed: _isSearching
? null
: () => _saveLocation("", _currentLatLng.latitude, _currentLatLng.longitude, performReverse: true),
child: _isSearching
? const CircularProgressIndicator(color: Colors.white)
: const Text("Diesen Standort wählen",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16)),
),
),
),
],
),
);
}
}