Files
rootandClaude Sonnet 5 e3aa088bc2 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>
2026-07-24 11:29:38 +02:00

111 lines
3.7 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import '../../../l10n/app_localizations.dart';
import '../../../models/emergency_number.dart';
import '../../../models/emergency_type.dart';
import '../../../services/dialer_service.dart';
/// Großer, gut antippbarer Button für eine einzelne [EmergencyNumber].
///
/// Löst den Anruf direkt über [DialerService] aus dafür ist kein
/// Callback nach oben nötig, da der Button keine weitere UI-Logik
/// beeinflusst. Kann der Wähler nicht geöffnet werden, zeigt der Button
/// die Nummer als einfachen Fallback in einer [SnackBar] an.
class EmergencyButton extends StatelessWidget {
const EmergencyButton({
super.key,
required this.emergencyNumber,
this.dialerService = const DialerService(),
});
final EmergencyNumber emergencyNumber;
final DialerService dialerService;
IconData get _icon {
switch (emergencyNumber.type) {
case EmergencyType.general:
return Icons.emergency;
case EmergencyType.police:
return Icons.local_police;
case EmergencyType.fire:
return Icons.local_fire_department;
case EmergencyType.ambulance:
return Icons.local_hospital;
case EmergencyType.seaRescue:
return Icons.directions_boat;
case EmergencyType.mountainRescue:
return Icons.terrain;
}
}
Future<void> _onTap(BuildContext context) async {
final success = await dialerService.call(emergencyNumber.number);
if (!success && context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(emergencyNumber.number)),
);
}
}
/// Wählt `labelDe`/`labelEn` (AP1-Datenfeld, keine ARB-Übersetzung die
/// Notrufnummern-Datenbasis kennt bewusst nur diese zwei Sprachen, siehe
/// `docs/plan.md` Abschnitt 4) passend zur aktuellen App-Locale aus.
String _localizedNumberLabel(BuildContext context) {
final languageCode = Localizations.localeOf(context).languageCode;
return languageCode == 'de' ? emergencyNumber.labelDe : emergencyNumber.labelEn;
}
@override
Widget build(BuildContext context) {
final label = _localizedNumberLabel(context);
final semanticLabel = AppLocalizations.of(
context,
)!.emergencyButtonSemanticLabel(label, emergencyNumber.number);
return Semantics(
button: true,
label: semanticLabel,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: SizedBox(
width: double.infinity,
height: 96,
child: ElevatedButton(
onPressed: () => _onTap(context),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
child: Tooltip(
message: semanticLabel,
child: Row(
children: [
Icon(_icon, size: 40),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
label,
style: Theme.of(context).textTheme.titleMedium,
),
Text(
emergencyNumber.number,
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
],
),
),
),
),
),
);
}
}