Files
votianlt/app/lib/settings_view.dart
Sven Carstensen 6e8bedd9b4 feat: Drag-and-Drop-Reihenfolge, Station-Abschluss-Flow und UI-Verbesserungen
Lieferstationen-Dialog (Backend/Vaadin):
- Aufgaben per Drag & Drop neu anordnen, inkl. Drag-Handle, komprimierter
  Kachelansicht während des Drags und horizontaler Einfügelinie als Drop-Target
- Drop-Indikator wird unterdrückt, wenn der Drop keine Positionsänderung bewirken
  würde, und nach dem Abschluss clientseitig zuverlässig aufgeräumt
- Drag-Handle, Aufgabentyp-Label und Close-Button auf einheitlicher Position
  ausgerichtet; Abstände in der Kachel komprimiert

Station-Abschluss-Flow (Flutter-App + Backend):
- Neuer Button "Station abschließen" unter den Aufgaben; deaktiviert, solange
  Pflichtaufgaben offen sind, ansonsten aktiv (auch wenn nur optionale Aufgaben
  existieren)
- Hinweisdialog nach Erledigung der letzten Pflichtaufgabe sowie Warnung bei
  offenen optionalen Aufgaben vor dem Senden
- Neue station_completed-Nachricht (jobId, jobNumber, stationOrder,
  completedAt, hasIncompleteOptionalTasks) wird an den Server gesendet
- Backend: Auftrag wird nicht mehr automatisch beim Erledigen der letzten
  Pflichtaufgabe abgeschlossen, sondern erst beim Empfang der
  station_completed-Nachricht (neuer Handler in MessageController und
  MessagingConfig)

Aufgabenliste in der App:
- Farbcodierung optionaler Aufgaben entfernt; stattdessen vertikal zentrierter
  "Optional"-Chip am rechten Kartenrand

Weitere UI-Überarbeitungen über Login, Jobs, Chats, Settings, Aufgaben-Capture-
Screens, Offline-Banner und zugehörige Widgets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:26:30 +02:00

224 lines
6.8 KiB
Dart

import 'package:flutter/material.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();
@override
void initState() {
super.initState();
_selectedLanguageCode = _appState.languageCode;
}
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: const Text('0.9.2'),
),
const Divider(height: 1, indent: 72),
],
),
);
}
}