Feature: Add and implement i18n support for theme settings in German and English, refactor AppSettings to DesignSettings, and improve settings UI structure.
This commit is contained in:
@@ -25,7 +25,14 @@
|
|||||||
"tooltipUserSettings": "Benutzer-Einstellungen",
|
"tooltipUserSettings": "Benutzer-Einstellungen",
|
||||||
"tooltipCollapseRail": "Leiste verkleinern",
|
"tooltipCollapseRail": "Leiste verkleinern",
|
||||||
"tooltipExpandRail": "Leiste erweitern",
|
"tooltipExpandRail": "Leiste erweitern",
|
||||||
"drawerSettings": "Einstellungen"
|
"drawerSettings": "Einstellungen",
|
||||||
|
"settings": {
|
||||||
|
"theme": {
|
||||||
|
"system": "System",
|
||||||
|
"light": "Hell",
|
||||||
|
"dark": "Dunkel"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Einstellungen",
|
"title": "Einstellungen",
|
||||||
|
|||||||
@@ -25,7 +25,14 @@
|
|||||||
"tooltipUserSettings": "User Settings",
|
"tooltipUserSettings": "User Settings",
|
||||||
"tooltipCollapseRail": "Collapse Rail",
|
"tooltipCollapseRail": "Collapse Rail",
|
||||||
"tooltipExpandRail": "Expand Rail",
|
"tooltipExpandRail": "Expand Rail",
|
||||||
"drawerSettings": "Settings"
|
"drawerSettings": "Settings",
|
||||||
|
"settings": {
|
||||||
|
"theme": {
|
||||||
|
"system": "System",
|
||||||
|
"light": "Light",
|
||||||
|
"dark": "Dark"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Settings",
|
"title": "Settings",
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
/// To regenerate, run: `dart run slang`
|
/// To regenerate, run: `dart run slang`
|
||||||
///
|
///
|
||||||
/// Locales: 2
|
/// Locales: 2
|
||||||
/// Strings: 126 (63 per locale)
|
/// Strings: 132 (66 per locale)
|
||||||
///
|
///
|
||||||
/// Built on 2025-09-27 at 12:19 UTC
|
/// Built on 2025-09-27 at 12:40 UTC
|
||||||
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint, unused_import
|
// ignore_for_file: type=lint, unused_import
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ class _TranslationsAppDe implements TranslationsAppEn {
|
|||||||
@override String get tooltipCollapseRail => 'Leiste verkleinern';
|
@override String get tooltipCollapseRail => 'Leiste verkleinern';
|
||||||
@override String get tooltipExpandRail => 'Leiste erweitern';
|
@override String get tooltipExpandRail => 'Leiste erweitern';
|
||||||
@override String get drawerSettings => 'Einstellungen';
|
@override String get drawerSettings => 'Einstellungen';
|
||||||
|
@override late final _TranslationsAppSettingsDe settings = _TranslationsAppSettingsDe._(_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path: settings
|
// Path: settings
|
||||||
@@ -131,6 +132,16 @@ class _TranslationsFeaturesDe implements TranslationsFeaturesEn {
|
|||||||
@override late final _TranslationsFeaturesReportsDe reports = _TranslationsFeaturesReportsDe._(_root);
|
@override late final _TranslationsFeaturesReportsDe reports = _TranslationsFeaturesReportsDe._(_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path: app.settings
|
||||||
|
class _TranslationsAppSettingsDe implements TranslationsAppSettingsEn {
|
||||||
|
_TranslationsAppSettingsDe._(this._root);
|
||||||
|
|
||||||
|
final TranslationsDe _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
@override late final _TranslationsAppSettingsThemeDe theme = _TranslationsAppSettingsThemeDe._(_root);
|
||||||
|
}
|
||||||
|
|
||||||
// Path: settings.sections
|
// Path: settings.sections
|
||||||
class _TranslationsSettingsSectionsDe implements TranslationsSettingsSectionsEn {
|
class _TranslationsSettingsSectionsDe implements TranslationsSettingsSectionsEn {
|
||||||
_TranslationsSettingsSectionsDe._(this._root);
|
_TranslationsSettingsSectionsDe._(this._root);
|
||||||
@@ -281,6 +292,18 @@ class _TranslationsFeaturesReportsDe implements TranslationsFeaturesReportsEn {
|
|||||||
@override String get description => 'Statistiken von Ausgaben';
|
@override String get description => 'Statistiken von Ausgaben';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path: app.settings.theme
|
||||||
|
class _TranslationsAppSettingsThemeDe implements TranslationsAppSettingsThemeEn {
|
||||||
|
_TranslationsAppSettingsThemeDe._(this._root);
|
||||||
|
|
||||||
|
final TranslationsDe _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
@override String get system => 'System';
|
||||||
|
@override String get light => 'Hell';
|
||||||
|
@override String get dark => 'Dunkel';
|
||||||
|
}
|
||||||
|
|
||||||
/// Flat map(s) containing all translations.
|
/// Flat map(s) containing all translations.
|
||||||
/// Only for edge cases! For simple maps, use the map function of this library.
|
/// Only for edge cases! For simple maps, use the map function of this library.
|
||||||
extension on TranslationsDe {
|
extension on TranslationsDe {
|
||||||
@@ -306,6 +329,9 @@ extension on TranslationsDe {
|
|||||||
case 'app.tooltipCollapseRail': return 'Leiste verkleinern';
|
case 'app.tooltipCollapseRail': return 'Leiste verkleinern';
|
||||||
case 'app.tooltipExpandRail': return 'Leiste erweitern';
|
case 'app.tooltipExpandRail': return 'Leiste erweitern';
|
||||||
case 'app.drawerSettings': return 'Einstellungen';
|
case 'app.drawerSettings': return 'Einstellungen';
|
||||||
|
case 'app.settings.theme.system': return 'System';
|
||||||
|
case 'app.settings.theme.light': return 'Hell';
|
||||||
|
case 'app.settings.theme.dark': return 'Dunkel';
|
||||||
case 'settings.title': return 'Einstellungen';
|
case 'settings.title': return 'Einstellungen';
|
||||||
case 'settings.sections.account': return 'Konto & Daten';
|
case 'settings.sections.account': return 'Konto & Daten';
|
||||||
case 'settings.sections.app': return 'App';
|
case 'settings.sections.app': return 'App';
|
||||||
|
|||||||
@@ -142,6 +142,8 @@ class TranslationsAppEn {
|
|||||||
|
|
||||||
/// en: 'Settings'
|
/// en: 'Settings'
|
||||||
String get drawerSettings => 'Settings';
|
String get drawerSettings => 'Settings';
|
||||||
|
|
||||||
|
late final TranslationsAppSettingsEn settings = TranslationsAppSettingsEn._(_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path: settings
|
// Path: settings
|
||||||
@@ -178,6 +180,16 @@ class TranslationsFeaturesEn {
|
|||||||
late final TranslationsFeaturesReportsEn reports = TranslationsFeaturesReportsEn._(_root);
|
late final TranslationsFeaturesReportsEn reports = TranslationsFeaturesReportsEn._(_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path: app.settings
|
||||||
|
class TranslationsAppSettingsEn {
|
||||||
|
TranslationsAppSettingsEn._(this._root);
|
||||||
|
|
||||||
|
final Translations _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
late final TranslationsAppSettingsThemeEn theme = TranslationsAppSettingsThemeEn._(_root);
|
||||||
|
}
|
||||||
|
|
||||||
// Path: settings.sections
|
// Path: settings.sections
|
||||||
class TranslationsSettingsSectionsEn {
|
class TranslationsSettingsSectionsEn {
|
||||||
TranslationsSettingsSectionsEn._(this._root);
|
TranslationsSettingsSectionsEn._(this._root);
|
||||||
@@ -412,6 +424,24 @@ class TranslationsFeaturesReportsEn {
|
|||||||
String get description => 'Statistics of expenses';
|
String get description => 'Statistics of expenses';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path: app.settings.theme
|
||||||
|
class TranslationsAppSettingsThemeEn {
|
||||||
|
TranslationsAppSettingsThemeEn._(this._root);
|
||||||
|
|
||||||
|
final Translations _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
|
||||||
|
/// en: 'System'
|
||||||
|
String get system => 'System';
|
||||||
|
|
||||||
|
/// en: 'Light'
|
||||||
|
String get light => 'Light';
|
||||||
|
|
||||||
|
/// en: 'Dark'
|
||||||
|
String get dark => 'Dark';
|
||||||
|
}
|
||||||
|
|
||||||
/// Flat map(s) containing all translations.
|
/// Flat map(s) containing all translations.
|
||||||
/// Only for edge cases! For simple maps, use the map function of this library.
|
/// Only for edge cases! For simple maps, use the map function of this library.
|
||||||
extension on Translations {
|
extension on Translations {
|
||||||
@@ -437,6 +467,9 @@ extension on Translations {
|
|||||||
case 'app.tooltipCollapseRail': return 'Collapse Rail';
|
case 'app.tooltipCollapseRail': return 'Collapse Rail';
|
||||||
case 'app.tooltipExpandRail': return 'Expand Rail';
|
case 'app.tooltipExpandRail': return 'Expand Rail';
|
||||||
case 'app.drawerSettings': return 'Settings';
|
case 'app.drawerSettings': return 'Settings';
|
||||||
|
case 'app.settings.theme.system': return 'System';
|
||||||
|
case 'app.settings.theme.light': return 'Light';
|
||||||
|
case 'app.settings.theme.dark': return 'Dark';
|
||||||
case 'settings.title': return 'Settings';
|
case 'settings.title': return 'Settings';
|
||||||
case 'settings.sections.account': return 'Account & Data';
|
case 'settings.sections.account': return 'Account & Data';
|
||||||
case 'settings.sections.app': return 'App';
|
case 'settings.sections.app': return 'App';
|
||||||
|
|||||||
@@ -295,8 +295,7 @@ class _DesktopDrawer extends StatelessWidget {
|
|||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
children: [
|
children: [
|
||||||
const _LogoHeader(),
|
// const Divider(),
|
||||||
const Divider(),
|
|
||||||
for (var i = 0; i < visibleItems.length; i++)
|
for (var i = 0; i < visibleItems.length; i++)
|
||||||
ListTile(
|
ListTile(
|
||||||
selected: i == safeSelected,
|
selected: i == safeSelected,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class DesignSettingsView extends StatelessWidget {
|
|||||||
final localeModel = context.read<LocaleController>();
|
final localeModel = context.read<LocaleController>();
|
||||||
|
|
||||||
return ChangeNotifierProvider(
|
return ChangeNotifierProvider(
|
||||||
create: (_) => AppSettingsViewModel(
|
create: (_) => DesignSettingsViewModel(
|
||||||
themeModel: themeModel,
|
themeModel: themeModel,
|
||||||
scaleModel: scaleModel,
|
scaleModel: scaleModel,
|
||||||
localeModel: localeModel,
|
localeModel: localeModel,
|
||||||
@@ -31,12 +31,11 @@ class _AppSettingsContent extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final vm = context.watch<AppSettingsViewModel>();
|
final vm = context.watch<DesignSettingsViewModel>();
|
||||||
final isLoading = vm.isLoading;
|
final isLoading = vm.isLoading;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
// const PanelHeader(title: 'App-Einstellungen'),
|
|
||||||
if (isLoading)
|
if (isLoading)
|
||||||
const Expanded(child: Center(child: CircularProgressIndicator()))
|
const Expanded(child: Center(child: CircularProgressIndicator()))
|
||||||
else
|
else
|
||||||
@@ -46,8 +45,8 @@ class _AppSettingsContent extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
_SystemBackgroundSection(),
|
_SystemBackgroundSection(),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
_TextScaleSection(),
|
// _TextScaleSection(),
|
||||||
const SizedBox(height: 16),
|
// const SizedBox(height: 16),
|
||||||
_LanguageSection(),
|
_LanguageSection(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -63,9 +62,42 @@ class _SystemBackgroundSection extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final t = Translations.of(context);
|
final t = Translations.of(context);
|
||||||
final vm = context.watch<AppSettingsViewModel>();
|
final vm = context.watch<DesignSettingsViewModel>();
|
||||||
final selected = vm.themeMode;
|
final selected = vm.themeMode;
|
||||||
|
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
final dividerColor = Theme.of(context).dividerColor;
|
||||||
|
|
||||||
|
Widget radioTile({
|
||||||
|
required bool isSelected,
|
||||||
|
required String label,
|
||||||
|
required VoidCallback onTap,
|
||||||
|
}) {
|
||||||
|
return ColoredBox(
|
||||||
|
color: cs.surface,
|
||||||
|
child: ListTile(
|
||||||
|
// contentPadding: const EdgeInsets.symmetric(
|
||||||
|
// // horizontal: 7,
|
||||||
|
// vertical: 0,
|
||||||
|
// ),
|
||||||
|
leading: Icon(
|
||||||
|
isSelected
|
||||||
|
? Icons.radio_button_checked
|
||||||
|
: Icons.radio_button_unchecked,
|
||||||
|
color: isSelected ? cs.primary : null,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
label,
|
||||||
|
style: isSelected
|
||||||
|
? const TextStyle(fontWeight: FontWeight.w600)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
onTap: onTap,
|
||||||
|
selected: isSelected,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -75,32 +107,24 @@ class _SystemBackgroundSection extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
|
||||||
ToggleButtons(
|
radioTile(
|
||||||
isSelected: [
|
isSelected: selected == ThemeMode.system,
|
||||||
selected == ThemeMode.system,
|
label: t.settings.app.systemDefault,
|
||||||
selected == ThemeMode.dark,
|
onTap: () => vm.setThemeMode(ThemeMode.system),
|
||||||
selected == ThemeMode.light,
|
),
|
||||||
],
|
Divider(height: 1, thickness: 1, color: dividerColor),
|
||||||
onPressed: (i) {
|
|
||||||
switch (i) {
|
radioTile(
|
||||||
case 0:
|
isSelected: selected == ThemeMode.dark,
|
||||||
vm.setThemeMode(ThemeMode.system);
|
label: t.settings.app.darkMode,
|
||||||
break;
|
onTap: () => vm.setThemeMode(ThemeMode.dark),
|
||||||
case 1:
|
),
|
||||||
vm.setThemeMode(ThemeMode.dark);
|
Divider(height: 1, thickness: 1, color: dividerColor),
|
||||||
break;
|
|
||||||
case 2:
|
radioTile(
|
||||||
vm.setThemeMode(ThemeMode.light);
|
isSelected: selected == ThemeMode.light,
|
||||||
break;
|
label: t.settings.app.lightMode,
|
||||||
}
|
onTap: () => vm.setThemeMode(ThemeMode.light),
|
||||||
},
|
|
||||||
borderRadius: BorderRadius.circular(24),
|
|
||||||
constraints: const BoxConstraints(minHeight: 44, minWidth: 140),
|
|
||||||
children: [
|
|
||||||
_SegItem(icon: Icons.phone_iphone, label: t.settings.app.systemDefault),
|
|
||||||
_SegItem(emoji: '🌙', label: t.settings.app.darkMode),
|
|
||||||
_SegItem(emoji: '☀️', label: t.settings.app.lightMode),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -137,13 +161,16 @@ class _TextScaleSection extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final t = Translations.of(context);
|
final t = Translations.of(context);
|
||||||
final vm = context.watch<AppSettingsViewModel>();
|
final vm = context.watch<DesignSettingsViewModel>();
|
||||||
final selected = vm.textScale;
|
final selected = vm.textScale;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(t.settings.app.textSize, style: const TextStyle(fontWeight: FontWeight.w600)),
|
Text(
|
||||||
|
t.settings.app.textSize,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
ToggleButtons(
|
ToggleButtons(
|
||||||
isSelected: [
|
isSelected: [
|
||||||
@@ -188,13 +215,16 @@ class _LanguageSection extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final t = Translations.of(context);
|
final t = Translations.of(context);
|
||||||
final vm = context.watch<AppSettingsViewModel>();
|
final vm = context.watch<DesignSettingsViewModel>();
|
||||||
final scheme = Theme.of(context).colorScheme;
|
final scheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(t.settings.app.language, style: Theme.of(context).textTheme.titleMedium),
|
Text(
|
||||||
|
t.settings.app.language,
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
DropdownButtonFormField<LanguagePref>(
|
DropdownButtonFormField<LanguagePref>(
|
||||||
@@ -205,8 +235,14 @@ class _LanguageSection extends StatelessWidget {
|
|||||||
value: LanguagePref.system,
|
value: LanguagePref.system,
|
||||||
child: Text(t.settings.app.systemDefault),
|
child: Text(t.settings.app.systemDefault),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(value: LanguagePref.de, child: Text(t.settings.app.german)),
|
DropdownMenuItem(
|
||||||
DropdownMenuItem(value: LanguagePref.en, child: Text(t.settings.app.english)),
|
value: LanguagePref.de,
|
||||||
|
child: Text(t.settings.app.german),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: LanguagePref.en,
|
||||||
|
child: Text(t.settings.app.english),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
isDense: true,
|
isDense: true,
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import 'package:app/core/ui/controller/scale_controller.dart';
|
|||||||
import 'package:app/core/ui/controller/theme.dart';
|
import 'package:app/core/ui/controller/theme.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class AppSettingsViewModel extends ChangeNotifier {
|
class DesignSettingsViewModel extends ChangeNotifier {
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
|
||||||
final ThemeController _theme;
|
final ThemeController _theme;
|
||||||
final ScaleController _scale;
|
final ScaleController _scale;
|
||||||
final LocaleController _locale;
|
final LocaleController _locale;
|
||||||
|
|
||||||
AppSettingsViewModel({
|
DesignSettingsViewModel({
|
||||||
required ThemeController themeModel,
|
required ThemeController themeModel,
|
||||||
required ScaleController scaleModel,
|
required ScaleController scaleModel,
|
||||||
required LocaleController localeModel,
|
required LocaleController localeModel,
|
||||||
|
|||||||
Reference in New Issue
Block a user