- Flutter/Dart (iOS + Android), Riverpod, go_router, i18n via ARB (deutsch) - Feature-Struktur: today, plants, locations, household (V2-Platzhalter), settings - Intervall-Modell: Aufgaben werden aus lastWatered/lastFertilized + Intervall berechnet - In-Memory-Demo-Daten, Firebase-Anbindung folgt im nächsten Block - Doku: Architektur, Firebase-Einrichtung, Forgejo-Git-Hosting Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
113 lines
3.7 KiB
Dart
113 lines
3.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../../core/settings/settings_provider.dart';
|
|
import '../../../core/widgets/app_drawer.dart';
|
|
import '../../../l10n/generated/app_localizations.dart';
|
|
|
|
class SettingsScreen extends ConsumerWidget {
|
|
const SettingsScreen({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final l10n = AppLocalizations.of(context);
|
|
final settings = ref.watch(settingsProvider);
|
|
final notifier = ref.read(settingsProvider.notifier);
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(title: Text(l10n.settingsTitle)),
|
|
drawer: const AppDrawer(),
|
|
body: ListView(
|
|
padding: const EdgeInsets.all(16),
|
|
children: [
|
|
_SectionTitle(l10n.settingsAppearance),
|
|
SegmentedButton<ThemeMode>(
|
|
segments: [
|
|
ButtonSegment(
|
|
value: ThemeMode.system,
|
|
label: Text(l10n.themeSystem),
|
|
icon: const Icon(Icons.brightness_auto),
|
|
),
|
|
ButtonSegment(
|
|
value: ThemeMode.light,
|
|
label: Text(l10n.themeLight),
|
|
icon: const Icon(Icons.light_mode),
|
|
),
|
|
ButtonSegment(
|
|
value: ThemeMode.dark,
|
|
label: Text(l10n.themeDark),
|
|
icon: const Icon(Icons.dark_mode),
|
|
),
|
|
],
|
|
selected: {settings.themeMode},
|
|
onSelectionChanged: (selection) =>
|
|
notifier.setThemeMode(selection.first),
|
|
),
|
|
const SizedBox(height: 24),
|
|
_SectionTitle(l10n.settingsTextSize),
|
|
SegmentedButton<AppTextSize>(
|
|
segments: [
|
|
ButtonSegment(
|
|
value: AppTextSize.normal,
|
|
label: Text(l10n.textSizeNormal),
|
|
),
|
|
ButtonSegment(
|
|
value: AppTextSize.large,
|
|
label: Text(l10n.textSizeLarge),
|
|
),
|
|
ButtonSegment(
|
|
value: AppTextSize.extraLarge,
|
|
label: Text(l10n.textSizeXLarge),
|
|
),
|
|
],
|
|
selected: {settings.textSize},
|
|
onSelectionChanged: (selection) =>
|
|
notifier.setTextSize(selection.first),
|
|
),
|
|
const SizedBox(height: 16),
|
|
SwitchListTile(
|
|
title: Text(l10n.settingsHighContrast),
|
|
value: settings.highContrast,
|
|
onChanged: notifier.setHighContrast,
|
|
contentPadding: EdgeInsets.zero,
|
|
),
|
|
const Divider(height: 32),
|
|
_SectionTitle(l10n.settingsReminderTime),
|
|
ListTile(
|
|
contentPadding: EdgeInsets.zero,
|
|
leading: const Icon(Icons.notifications_active),
|
|
title: Text(
|
|
settings.reminderTime.format(context),
|
|
style: Theme.of(context).textTheme.titleLarge,
|
|
),
|
|
subtitle: Text(l10n.settingsReminderTimeHint),
|
|
trailing: const Icon(Icons.chevron_right),
|
|
onTap: () async {
|
|
final picked = await showTimePicker(
|
|
context: context,
|
|
initialTime: settings.reminderTime,
|
|
);
|
|
if (picked != null) {
|
|
notifier.setReminderTime(picked);
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _SectionTitle extends StatelessWidget {
|
|
const _SectionTitle(this.title);
|
|
|
|
final String title;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 12),
|
|
child: Text(title, style: Theme.of(context).textTheme.titleMedium),
|
|
);
|
|
}
|
|
}
|