import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../l10n/app_localizations.dart'; import '../../models/country.dart'; import '../../models/emergency_number.dart'; import '../../models/emergency_type.dart'; import '../../providers/emergency_provider.dart'; import '../../providers/emergency_status.dart'; import 'country_picker_screen.dart'; import 'widgets/country_header.dart'; import 'widgets/emergency_button.dart'; /// Startbildschirm mit Länder-Header und Notruf-Buttons. /// /// AP5 (State-Integration): Land und Ladezustand kommen jetzt aus dem /// [EmergencyProvider] (SIM/GPS/Cache-Ermittlungskette aus AP4), nicht mehr /// aus einem hartkodierten ISO-Code (AP3). class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { @override void initState() { super.initState(); // Einmaliger Start der Ermittlungskette nach dem ersten Frame: init() // ruft synchron (vor dem ersten await) notifyListeners() auf, was // während der laufenden Baumkonstruktion (initState) zu "setState() // called during build" führen würde – daher erst im Post-Frame-Callback. WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) context.read().init(); }); } /// Globaler Panikmodus-Button (Plan Abschnitt 2 + 6): 112 funktioniert als /// GSM-weiter Fallback in den meisten Mobilfunknetzen und muss unabhängig /// vom Lade-/Fehlerzustand der Ländererkennung immer erreichbar sein. static const _panicNumber = EmergencyNumber( type: EmergencyType.general, number: '112', labelDe: 'Internationaler Notruf', labelEn: 'International Emergency', ); /// True, wenn das erkannte Land bereits eine `general`-Nummer "112" führt /// (z. B. DE) – dann würde der Panik-Button dieselbe Nummer doppelt zeigen. bool _countryAlreadyHasPanicNumber(Country country) { return country.numbers.any( (n) => n.type == EmergencyType.general && n.number == _panicNumber.number, ); } bool _showPanicButton(EmergencyProvider provider) { final country = provider.country; if (provider.status == EmergencyStatus.ready && country != null) { return !_countryAlreadyHasPanicNumber(country); } // Ladezustand, Fehler, countryUnknown oder Land nicht in Datenbasis: // Panik-Button ist die einzig sichere Konstante, also immer sichtbar. return true; } /// Öffnet den `CountryPickerScreen` (Plan Abschnitt 7 + 8, AP6: manuelle /// Länderauswahl als Fallback, wenn das Land nicht automatisch ermittelbar /// ist bzw. jederzeit als bewusste Nutzerentscheidung). void _openCountryPicker(BuildContext context) { Navigator.of(context).push(MaterialPageRoute(builder: (_) => const CountryPickerScreen())); } Widget _buildContent(BuildContext context, EmergencyProvider provider) { final l10n = AppLocalizations.of(context)!; switch (provider.status) { case EmergencyStatus.initializing: case EmergencyStatus.locating: return const Center(child: CircularProgressIndicator()); case EmergencyStatus.error: return _StatusMessage( message: provider.errorMessage ?? l10n.errorGeneric, retryLabel: l10n.retryButton, selectCountryLabel: l10n.selectCountryButton, onRetry: () => context.read().retry(), onSelectCountry: () => _openCountryPicker(context), ); // permissionDenied wird bewusst wie countryUnknown behandelt, siehe // Doc-Kommentar in EmergencyProvider. case EmergencyStatus.permissionDenied: case EmergencyStatus.countryUnknown: return _StatusMessage( message: l10n.countryUnknownMessage, retryLabel: l10n.retryButton, selectCountryLabel: l10n.selectCountryButton, onRetry: () => context.read().retry(), onSelectCountry: () => _openCountryPicker(context), ); case EmergencyStatus.ready: final country = provider.country; if (country == null) { return _StatusMessage(message: l10n.countryNotInDatabaseMessage); } return _CountryContent(country: country); } } @override Widget build(BuildContext context) { final provider = context.watch(); final l10n = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar( title: Text(l10n.appTitle), actions: [ IconButton( key: const Key('countryPickerButton'), icon: const Icon(Icons.public), tooltip: l10n.selectCountryButton, onPressed: () => _openCountryPicker(context), ), ], ), body: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (_showPanicButton(provider)) EmergencyButton( key: const Key('panicButton'), emergencyNumber: _panicNumber, ), Expanded(child: _buildContent(context, provider)), ], ), ), ), ); } } /// Kurzer Hinweistext für Fehler-/Unknown-/Fallback-Zustände, optional mit /// "Erneut versuchen"-Button. class _StatusMessage extends StatelessWidget { const _StatusMessage({ required this.message, this.retryLabel, this.selectCountryLabel, this.onRetry, this.onSelectCountry, }); final String message; final String? retryLabel; final String? selectCountryLabel; final VoidCallback? onRetry; /// Öffnet den `CountryPickerScreen`, damit der Nutzer sein Land manuell /// auswählen kann, wenn es nicht automatisch ermittelbar war (Plan /// Abschnitt 7, "Land nicht ermittelbar → Panik-Button + Länder-Suchfeld"). final VoidCallback? onSelectCountry; @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(message, textAlign: TextAlign.center), if (onRetry != null) ...[ const SizedBox(height: 12), ElevatedButton(onPressed: onRetry, child: Text(retryLabel!)), ], if (onSelectCountry != null) ...[ const SizedBox(height: 12), TextButton.icon( onPressed: onSelectCountry, icon: const Icon(Icons.public), label: Text(selectCountryLabel!), ), ], ], ), ), ); } } /// Länder-Header + sortierte Liste der landesspezifischen Notruf-Buttons. class _CountryContent extends StatelessWidget { const _CountryContent({required this.country}); final Country country; @override Widget build(BuildContext context) { // general zuoberst, danach der Rest in JSON-Reihenfolge. final numbers = [ ...country.numbers.where((n) => n.type == EmergencyType.general), ...country.numbers.where((n) => n.type != EmergencyType.general), ]; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ CountryHeader(country: country), Expanded( child: ListView.builder( itemCount: numbers.length, itemBuilder: (context, index) { final number = numbers[index]; return EmergencyButton( key: ValueKey(number.type), emergencyNumber: number, ); }, ), ), ], ); } }