Refactor: Introduce LocaleController and ScaleController, unify text and language settings, and simplify AppSettingsView structure.
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import 'package:app/core/ui/controller/locale_controller.dart';
|
||||
import 'package:app/core/ui/controller/scale_controller.dart';
|
||||
import 'package:app/core/ui/controller/theme.dart';
|
||||
import 'package:app/modules/settings/modules/app/model/app_settings_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -8,9 +10,16 @@ class AppSettingsView extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeModel = context.watch<ThemeController>();
|
||||
final themeModel = context.read<ThemeController>();
|
||||
final scaleModel = context.read<ScaleController>();
|
||||
final localeModel = context.read<LocaleController>();
|
||||
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => AppSettingsViewModel(themeModel: themeModel)..load(),
|
||||
create: (_) => AppSettingsViewModel(
|
||||
themeModel: themeModel,
|
||||
scaleModel: scaleModel,
|
||||
localeModel: localeModel,
|
||||
)..load(),
|
||||
child: const _AppSettingsContent(),
|
||||
);
|
||||
}
|
||||
@@ -36,7 +45,7 @@ class _AppSettingsContent extends StatelessWidget {
|
||||
children: [
|
||||
_SystemBackgroundSection(),
|
||||
const SizedBox(height: 16),
|
||||
_TextSizeSection(),
|
||||
_TextScaleSection(),
|
||||
const SizedBox(height: 16),
|
||||
_LanguageSection(),
|
||||
],
|
||||
@@ -73,13 +82,13 @@ class _SystemBackgroundSection extends StatelessWidget {
|
||||
onPressed: (i) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
vm.setBackgroundPref(ThemeMode.system);
|
||||
vm.setThemeMode(ThemeMode.system);
|
||||
break;
|
||||
case 1:
|
||||
vm.setBackgroundPref(ThemeMode.dark);
|
||||
vm.setThemeMode(ThemeMode.dark);
|
||||
break;
|
||||
case 2:
|
||||
vm.setBackgroundPref(ThemeMode.light);
|
||||
vm.setThemeMode(ThemeMode.light);
|
||||
break;
|
||||
}
|
||||
},
|
||||
@@ -120,36 +129,59 @@ class _SegItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _TextSizeSection extends StatelessWidget {
|
||||
class _TextScaleSection extends StatelessWidget {
|
||||
const _TextScaleSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final vm = context.watch<AppSettingsViewModel>();
|
||||
final selected = vm.textSize;
|
||||
|
||||
Widget radio(AppTextSize size, String label, String sub) {
|
||||
return RadioListTile<AppTextSize>(
|
||||
value: size,
|
||||
groupValue: selected,
|
||||
onChanged: (v) => vm.setTextSize(v ?? AppTextSize.normal),
|
||||
title: Text(label),
|
||||
subtitle: Text(sub),
|
||||
);
|
||||
}
|
||||
final selected = vm.textScale;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Textgröße', style: Theme.of(context).textTheme.titleMedium),
|
||||
const Text('Textgröße', style: TextStyle(fontWeight: FontWeight.w600)),
|
||||
const SizedBox(height: 8),
|
||||
radio(AppTextSize.small, 'Klein', 'Kompakte Darstellung'),
|
||||
radio(AppTextSize.normal, 'Standard', 'Empfohlene Einstellung'),
|
||||
radio(AppTextSize.large, 'Groß', 'Größere, besser lesbare Texte'),
|
||||
ToggleButtons(
|
||||
isSelected: [
|
||||
selected == TextScalePref.system,
|
||||
selected == TextScalePref.small,
|
||||
selected == TextScalePref.medium,
|
||||
selected == TextScalePref.large,
|
||||
],
|
||||
onPressed: (i) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
vm.setTextScale(TextScalePref.system);
|
||||
break;
|
||||
case 1:
|
||||
vm.setTextScale(TextScalePref.small);
|
||||
break;
|
||||
case 2:
|
||||
vm.setTextScale(TextScalePref.medium);
|
||||
break;
|
||||
case 3:
|
||||
vm.setTextScale(TextScalePref.large);
|
||||
break;
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
constraints: const BoxConstraints(minHeight: 44, minWidth: 120),
|
||||
children: const [
|
||||
_SegItem(icon: Icons.phone_android, label: 'System'),
|
||||
_SegItem(icon: Icons.text_fields, label: 'Klein'),
|
||||
_SegItem(icon: Icons.text_fields, label: 'Mittel'),
|
||||
_SegItem(icon: Icons.text_fields, label: 'Groß'),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LanguageSection extends StatelessWidget {
|
||||
const _LanguageSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final vm = context.watch<AppSettingsViewModel>();
|
||||
@@ -160,18 +192,24 @@ class _LanguageSection extends StatelessWidget {
|
||||
children: [
|
||||
Text('Sprache', style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 8),
|
||||
DropdownButtonFormField<AppLanguage>(
|
||||
value: vm.language,
|
||||
onChanged: (v) => vm.setLanguage(v ?? AppLanguage.de),
|
||||
|
||||
DropdownButtonFormField<LanguagePref>(
|
||||
initialValue: vm.language,
|
||||
onChanged: (v) => vm.setLanguage(v ?? LanguagePref.en),
|
||||
items: const [
|
||||
DropdownMenuItem(value: AppLanguage.de, child: Text('Deutsch')),
|
||||
DropdownMenuItem(value: AppLanguage.en, child: Text('Englisch')),
|
||||
DropdownMenuItem(
|
||||
value: LanguagePref.system,
|
||||
child: Text('Systemstandard'),
|
||||
),
|
||||
DropdownMenuItem(value: LanguagePref.de, child: Text('Deutsch')),
|
||||
DropdownMenuItem(value: LanguagePref.en, child: Text('Englisch')),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Änderungen wirken sich nach dem Speichern aus.',
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import 'package:app/core/ui/controller/locale_controller.dart';
|
||||
import 'package:app/core/ui/controller/scale_controller.dart';
|
||||
import 'package:app/core/ui/controller/theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Domain-ish enums for clarity and easy (de)serialization.
|
||||
enum AppTextSize { small, normal, large }
|
||||
|
||||
enum AppLanguage { de, en }
|
||||
|
||||
class AppSettingsViewModel extends ChangeNotifier {
|
||||
/// Use `null` to represent "system default background".
|
||||
Color? _backgroundColorSystem;
|
||||
AppTextSize _textSize = AppTextSize.normal;
|
||||
AppLanguage _language = AppLanguage.de;
|
||||
|
||||
bool _isLoading = false;
|
||||
bool _isSaving = false;
|
||||
|
||||
final ThemeController _theme;
|
||||
final ScaleController _scale;
|
||||
final LocaleController _locale;
|
||||
|
||||
AppSettingsViewModel({required ThemeController themeModel})
|
||||
: _theme = themeModel;
|
||||
AppSettingsViewModel({
|
||||
required ThemeController themeModel,
|
||||
required ScaleController scaleModel,
|
||||
required LocaleController localeModel,
|
||||
}) : _theme = themeModel,
|
||||
_scale = scaleModel,
|
||||
_locale = localeModel;
|
||||
|
||||
/// Pretend to load from backend. Plug your repository here later.
|
||||
Future<void> load() async {
|
||||
@@ -28,80 +27,30 @@ class AppSettingsViewModel extends ChangeNotifier {
|
||||
// TODO: Replace with real backend call.
|
||||
await Future<void>.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
// Example defaults (could come from server response).
|
||||
_backgroundColorSystem = null; // null => Systemstandard
|
||||
_textSize = AppTextSize.normal;
|
||||
_language = AppLanguage.de;
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setBackgroundPref(ThemeMode mode) {
|
||||
void setThemeMode(ThemeMode mode) {
|
||||
_theme.setTheme(mode);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Save to backend (stub).
|
||||
Future<void> save() async {
|
||||
if (_isSaving) return;
|
||||
_isSaving = true;
|
||||
notifyListeners();
|
||||
|
||||
// TODO: Replace with real backend update.
|
||||
await Future<void>.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
_isSaving = false;
|
||||
void setTextScale(TextScalePref pref) {
|
||||
_scale.setScale(pref);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setSystemBackgroundColor(Color? colorOrNullForSystem) {
|
||||
_backgroundColorSystem = colorOrNullForSystem;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setTextSize(AppTextSize size) {
|
||||
_textSize = size;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setLanguage(AppLanguage lang) {
|
||||
_language = lang;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'backgroundColorSystem': _backgroundColorSystem?.value,
|
||||
'textSize': _textSize.name,
|
||||
'language': _language.name,
|
||||
};
|
||||
|
||||
void fromJson(Map<String, dynamic> json) {
|
||||
final int? colorValue = json['backgroundColorSystem'] as int?;
|
||||
_backgroundColorSystem = colorValue != null ? Color(colorValue) : null;
|
||||
|
||||
final ts = json['textSize'] as String?;
|
||||
_textSize = AppTextSize.values.firstWhere(
|
||||
(e) => e.name == ts,
|
||||
orElse: () => AppTextSize.normal,
|
||||
);
|
||||
|
||||
final lng = json['language'] as String?;
|
||||
_language = AppLanguage.values.firstWhere(
|
||||
(e) => e.name == lng,
|
||||
orElse: () => AppLanguage.de,
|
||||
);
|
||||
|
||||
void setLanguage(LanguagePref lang) {
|
||||
_locale.setLanguage(lang);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
ThemeMode get themeMode => _theme.themeMode;
|
||||
|
||||
Color? get backgroundColorSystem => _backgroundColorSystem;
|
||||
LanguagePref? get language => _locale.language;
|
||||
|
||||
AppTextSize get textSize => _textSize;
|
||||
|
||||
AppLanguage get language => _language;
|
||||
TextScalePref get textScale => _scale.scale;
|
||||
|
||||
bool get isLoading => _isLoading;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user