Files
votianlt/app/lib/settings_view.dart
Sven Carstensen f4c2fa6ba3 feat: Rechnungsfilter (offen/gesendet), Rabatt-Dialog, Adress-Labels mit voller Adresse und Duplikatsschutz im Adressbuch
- Rechnungen erstellen: Filter Alle/Offene/Gesendete pro Abrechnungsmonat; Versand wird in system_invoice_dispatch protokolliert
- Rabatt pro Kunde jetzt über Icon-Dialog mit Prozentwert und Grund; Grund erscheint auf der Rechnungsposition
- MessageController: stille catch-Blöcke durch Logging ersetzt, offene Pflicht-Tasks werden bei station_completed geloggt
- Auftragserstellung: Abhol- und Lieferadresslisten zeigen die komplette Adresse, Auftraggeber nur den Firmennamen; (2)-Zähler entfernt
- Adressbuch: komplett identische Einträge werden beim Anlegen übersprungen bzw. mit Hinweis abgelehnt
- App: Versionsanzeige liest zur Laufzeit aus dem Build (package_info_plus), pubspec auf 0.9.20+1; Versionspflege dokumentiert (pom.xml fürs Web, pubspec.yaml für die App)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:41:21 +02:00

237 lines
7.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'l10n/app_localizations.dart';
import 'app_state.dart';
import 'app_theme.dart';
/// Supported languages with their display names and flag emojis
class LanguageOption {
final String code;
final String name;
final String flagEmoji;
const LanguageOption({
required this.code,
required this.name,
required this.flagEmoji,
});
}
class SettingsView extends StatefulWidget {
const SettingsView({super.key});
@override
State<SettingsView> createState() => _SettingsViewState();
}
class _SettingsViewState extends State<SettingsView> {
late String _selectedLanguageCode;
final AppState _appState = AppState();
String _appVersion = '';
@override
void initState() {
super.initState();
_selectedLanguageCode = _appState.languageCode;
_loadAppVersion();
}
Future<void> _loadAppVersion() async {
final info = await PackageInfo.fromPlatform();
if (!mounted) {
return;
}
setState(() {
_appVersion = info.version;
});
}
void _onLanguageSelected(String languageCode) async {
setState(() {
_selectedLanguageCode = languageCode;
});
// Save language preference
await _appState.setLanguage(languageCode);
// Show confirmation snackbar
_showLanguageChangedSnackBar(languageCode);
}
void _showLanguageChangedSnackBar(String languageCode) {
final l10n = AppLocalizations.of(context);
// Get the language name from the corresponding localization
String languageName;
String flagEmoji;
switch (languageCode) {
case 'de':
languageName = 'Deutsch';
flagEmoji = '🇩🇪';
break;
case 'en':
languageName = 'English';
flagEmoji = '🇬🇧';
break;
case 'es':
languageName = 'Español';
flagEmoji = '🇪🇸';
break;
case 'fr':
languageName = 'Français';
flagEmoji = '🇫🇷';
break;
case 'pl':
languageName = 'Polski';
flagEmoji = '🇵🇱';
break;
case 'ru':
languageName = 'Русский';
flagEmoji = '🇷🇺';
break;
case 'tr':
languageName = 'Türkçe';
flagEmoji = '🇹🇷';
break;
case 'et':
languageName = 'Eesti';
flagEmoji = '🇪🇪';
break;
case 'lv':
languageName = 'Latviešu';
flagEmoji = '🇱🇻';
break;
case 'lt':
languageName = 'Lietuvių';
flagEmoji = '🇱🇹';
break;
default:
languageName = languageCode;
flagEmoji = '🌐';
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('${l10n.languageChanged}: $flagEmoji $languageName'),
duration: const Duration(seconds: 2),
backgroundColor: AppColors.success,
),
);
}
/// Get all available language options with their localized names
List<LanguageOption> _getLanguageOptions() {
return [
const LanguageOption(code: 'de', name: 'Deutsch', flagEmoji: '🇩🇪'),
const LanguageOption(code: 'en', name: 'English', flagEmoji: '🇬🇧'),
const LanguageOption(code: 'es', name: 'Español', flagEmoji: '🇪🇸'),
const LanguageOption(code: 'fr', name: 'Français', flagEmoji: '🇫🇷'),
const LanguageOption(code: 'pl', name: 'Polski', flagEmoji: '🇵🇱'),
const LanguageOption(code: 'ru', name: 'Русский', flagEmoji: '🇷🇺'),
const LanguageOption(code: 'tr', name: 'Türkçe', flagEmoji: '🇹🇷'),
const LanguageOption(code: 'et', name: 'Eesti', flagEmoji: '🇪🇪'),
const LanguageOption(code: 'lv', name: 'Latviešu', flagEmoji: '🇱🇻'),
const LanguageOption(code: 'lt', name: 'Lietuvių', flagEmoji: '🇱🇹'),
];
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
final languageOptions = _getLanguageOptions();
return Scaffold(
appBar: AppBar(title: Text(l10n.settings)),
body: ListView(
children: [
// Language Selection Section
Padding(
padding: const EdgeInsets.fromLTRB(16, 24, 16, 8),
child: Text(
l10n.language.toUpperCase(),
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.textMuted,
letterSpacing: 1.2,
),
),
),
const Divider(height: 1),
// Language List
...languageOptions.map((language) {
final isSelected = language.code == _selectedLanguageCode;
return Column(
children: [
ListTile(
leading: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: AppColors.surfaceMuted,
borderRadius: BorderRadius.circular(20),
),
child: Center(
child: Text(
language.flagEmoji,
style: const TextStyle(fontSize: 24),
),
),
),
title: Text(
language.name,
style: TextStyle(
fontWeight:
isSelected ? FontWeight.w600 : FontWeight.normal,
color:
isSelected
? AppColors.primaryStrong
: AppColors.textStrong,
),
),
trailing:
isSelected
? const Icon(
Icons.check_circle,
color: AppColors.primary,
)
: const Icon(
Icons.circle_outlined,
color: AppColors.textMuted,
),
onTap: () => _onLanguageSelected(language.code),
selected: isSelected,
selectedTileColor: AppColors.primarySoft,
),
const Divider(height: 1, indent: 72),
],
);
}),
// App Info Section
Padding(
padding: const EdgeInsets.fromLTRB(16, 32, 16, 8),
child: Text(
l10n.appInfo,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.textMuted,
letterSpacing: 1.2,
),
),
),
const Divider(height: 1),
ListTile(
leading: Icon(Icons.info_outline, color: AppColors.textMuted),
title: Text(l10n.version),
subtitle: Text(_appVersion),
),
const Divider(height: 1, indent: 72),
],
),
);
}
}