m3mo cb308bbf68 Initial project setup with Clean Architecture
- Flutter frontend with Provider state management
- FastAPI backend with SQLAlchemy ORM
- Internationalization support (EN/DE)
- Clean Architecture folder structure
- GoRouter for navigation
- GetIt for dependency injection
2026-02-02 16:43:37 +01:00

182 lines
5.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import '../viewmodels/settings_viewmodel.dart';
class SettingsPage extends StatelessWidget {
const SettingsPage({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final settingsVm = context.watch<SettingsViewModel>();
return Scaffold(
appBar: AppBar(
title: Text(l10n.settings),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => context.pop(),
),
),
body: ListView(
children: [
_SectionHeader(title: l10n.general),
ListTile(
leading: const Icon(Icons.language),
title: Text(l10n.language),
subtitle: Text(
settingsVm.locale != null
? settingsVm.getLanguageName(settingsVm.locale!)
: l10n.systemDefault,
),
trailing: const Icon(Icons.chevron_right),
onTap: () => _showLanguageDialog(context, settingsVm, l10n),
),
const Divider(),
_SectionHeader(title: l10n.appearance),
ListTile(
leading: const Icon(Icons.dark_mode),
title: Text(l10n.darkMode),
subtitle: Text(_getThemeModeLabel(settingsVm.themeMode, l10n)),
trailing: const Icon(Icons.chevron_right),
onTap: () => _showThemeDialog(context, settingsVm, l10n),
),
const Divider(),
_SectionHeader(title: l10n.about),
ListTile(
leading: const Icon(Icons.info_outline),
title: Text(l10n.version),
subtitle: const Text('1.0.0'),
),
],
),
);
}
String _getThemeModeLabel(ThemeMode mode, AppLocalizations l10n) {
switch (mode) {
case ThemeMode.system:
return l10n.systemDefault;
case ThemeMode.light:
return l10n.lightMode;
case ThemeMode.dark:
return l10n.darkModeOption;
}
}
void _showLanguageDialog(
BuildContext context,
SettingsViewModel vm,
AppLocalizations l10n,
) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(l10n.language),
content: Column(
mainAxisSize: MainAxisSize.min,
children: SettingsViewModel.supportedLocales.map((locale) {
return RadioListTile<Locale>(
title: Text(vm.getLanguageName(locale)),
value: locale,
groupValue: vm.locale,
onChanged: (value) {
if (value != null) {
vm.setLocale(value);
Navigator.pop(context);
}
},
);
}).toList(),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(l10n.cancel),
),
],
),
);
}
void _showThemeDialog(
BuildContext context,
SettingsViewModel vm,
AppLocalizations l10n,
) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(l10n.darkMode),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<ThemeMode>(
title: Text(l10n.systemDefault),
value: ThemeMode.system,
groupValue: vm.themeMode,
onChanged: (value) {
if (value != null) {
vm.setThemeMode(value);
Navigator.pop(context);
}
},
),
RadioListTile<ThemeMode>(
title: Text(l10n.lightMode),
value: ThemeMode.light,
groupValue: vm.themeMode,
onChanged: (value) {
if (value != null) {
vm.setThemeMode(value);
Navigator.pop(context);
}
},
),
RadioListTile<ThemeMode>(
title: Text(l10n.darkModeOption),
value: ThemeMode.dark,
groupValue: vm.themeMode,
onChanged: (value) {
if (value != null) {
vm.setThemeMode(value);
Navigator.pop(context);
}
},
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(l10n.cancel),
),
],
),
);
}
}
class _SectionHeader extends StatelessWidget {
final String title;
const _SectionHeader({required this.title});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
title.toUpperCase(),
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
);
}
}