Notrufnummern-Datenbasis, Ländererkennung, State-Management und Lokalisierung (AP1-AP7)

Implementiert den kompletten Kern-Flow der Emergency App gemäß docs/plan.md:
- Notrufnummern-Datenbasis für 199 Länder + Modelle/Repository (AP1)
- DialerService für tel:-Notrufe ohne Auto-Dial (AP2)
- Statisches UI mit Panik-Button-Fallback (AP3)
- Priorisierte Ländererkennung: SIM/Netz -> GPS/Geocoding -> Cache (AP4)
- EmergencyProvider verdrahtet Repository/LocationService mit der UI (AP5)
- Manuelle Länderauswahl als Fallback samt eigener Priorität (AP6)
- UI-Lokalisierung (de/en/fr/es), an erkanntes Land gekoppelt (AP7)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
root
2026-07-24 11:29:38 +02:00
co-authored by Claude Sonnet 5
parent 40e95cc495
commit e3aa088bc2
54 changed files with 7550 additions and 145 deletions
@@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../l10n/app_localizations.dart';
import '../../models/country.dart';
import '../../providers/emergency_provider.dart';
/// Durchsuchbare Länderliste zur manuellen Länderauswahl
/// (`docs/plan.md`, Abschnitt 7 + 8, AP6).
///
/// Tap auf ein Land ruft [EmergencyProvider.selectCountryManually] auf
/// das persistiert die Auswahl und stößt die Ermittlungskette danach sofort
/// erneut an und schließt den Screen.
class CountryPickerScreen extends StatefulWidget {
const CountryPickerScreen({super.key});
@override
State<CountryPickerScreen> createState() => _CountryPickerScreenState();
}
class _CountryPickerScreenState extends State<CountryPickerScreen> {
final _searchController = TextEditingController();
String _query = '';
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
List<Country> _filtered(List<Country> countries) {
final query = _query.trim().toLowerCase();
if (query.isEmpty) return countries;
return countries
.where(
(country) =>
country.nameDe.toLowerCase().contains(query) ||
country.nameEn.toLowerCase().contains(query) ||
country.isoCode.toLowerCase().contains(query),
)
.toList(growable: false);
}
Future<void> _select(BuildContext context, Country country) async {
await context.read<EmergencyProvider>().selectCountryManually(country.isoCode);
if (context.mounted) Navigator.of(context).pop();
}
@override
Widget build(BuildContext context) {
final repository = context.read<EmergencyProvider>().repository;
final countries = _filtered(repository.allCountries);
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(title: Text(l10n.pickerTitle)),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: TextField(
controller: _searchController,
autofocus: true,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
hintText: l10n.pickerSearchHint,
border: const OutlineInputBorder(),
),
onChanged: (value) => setState(() => _query = value),
),
),
Expanded(
child: countries.isEmpty
? Center(child: Text(l10n.pickerNoResults))
: ListView.builder(
itemCount: countries.length,
itemBuilder: (context, index) {
final country = countries[index];
return ListTile(
key: ValueKey(country.isoCode),
leading: Text(country.flag, style: const TextStyle(fontSize: 24)),
title: Text(country.nameDe),
subtitle: Text(country.isoCode),
onTap: () => _select(context, country),
);
},
),
),
],
),
);
}
}