Files
eryao/apps/lib/features/settings/presentation/screens/general_settings_screen.dart
T

333 lines
11 KiB
Dart
Raw Normal View History

2026-04-03 16:56:47 +08:00
import 'package:flutter/material.dart';
import '../../../../l10n/app_localizations.dart';
import '../../../../shared/theme/design_tokens.dart';
import '../../data/models/profile_settings.dart';
import '../widgets/settings_section_widgets.dart';
import 'language_settings_screen.dart';
2026-04-03 16:56:47 +08:00
class GeneralSettingsScreen extends StatefulWidget {
const GeneralSettingsScreen({
super.key,
required this.settings,
required this.onSettingsChanged,
2026-04-03 16:56:47 +08:00
});
final ProfileSettingsV1 settings;
final Future<void> Function(ProfileSettingsV1 settings) onSettingsChanged;
2026-04-03 16:56:47 +08:00
@override
State<GeneralSettingsScreen> createState() => _GeneralSettingsScreenState();
}
class _GeneralSettingsScreenState extends State<GeneralSettingsScreen> {
late ProfileSettingsV1 _settings;
@override
void initState() {
super.initState();
_settings = widget.settings;
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final colors = Theme.of(context).colorScheme;
return PopScope<ProfileSettingsV1>(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
if (didPop) {
return;
}
Navigator.of(context).pop(_settings);
},
child: Scaffold(
backgroundColor: colors.surfaceContainerLow,
appBar: AppBar(
leading: IconButton(
onPressed: () => Navigator.of(context).pop(_settings),
icon: const Icon(Icons.arrow_back_ios_new_rounded),
),
title: Text(l10n.settingsGeneralTitle),
centerTitle: true,
backgroundColor: colors.surfaceContainerLow,
surfaceTintColor: colors.surfaceContainerLow,
),
body: ListView(
padding: const EdgeInsets.all(AppSpacing.lg),
children: [
SectionLabel(text: l10n.settingsSectionGeneral),
SettingsGroupCard(
children: [
SettingsMenuTile(
icon: Icons.language_rounded,
title: l10n.settingsInterfaceLanguage,
2026-04-03 16:56:47 +08:00
subtitle: displayLanguageLabel(
l10n,
_settings.preferences.interfaceLanguage,
),
tint: colors.primary,
background: colors.surfaceContainerHighest,
onTap: () => _selectLanguage(
_settings.preferences.interfaceLanguage,
(lang) => setState(() {
_settings = _settings.copyWith(
preferences: _settings.preferences.copyWith(
interfaceLanguage: lang,
),
);
}),
),
),
SettingsMenuTile(
icon: Icons.smart_toy_rounded,
title: l10n.settingsAiLanguage,
subtitle: displayLanguageLabel(
l10n,
_settings.preferences.aiLanguage,
),
tint: colors.primary,
background: colors.surfaceContainerHighest,
showDivider: true,
onTap: () => _selectLanguage(
_settings.preferences.aiLanguage,
(lang) => setState(() {
_settings = _settings.copyWith(
preferences: _settings.preferences.copyWith(
aiLanguage: lang,
),
);
}),
),
),
SettingsMenuTile(
icon: Icons.access_time_rounded,
title: l10n.settingsTimezone,
subtitle: _settings.preferences.timezone,
tint: colors.primary,
background: colors.surfaceContainerHighest,
showDivider: true,
onTap: () => _selectTimezone(context),
),
SettingsMenuTile(
icon: Icons.public_rounded,
title: l10n.settingsCountry,
subtitle: _settings.preferences.country,
tint: colors.primary,
background: colors.surfaceContainerHighest,
showDivider: false,
onTap: () => _selectCountry(context),
),
],
),
const SizedBox(height: AppSpacing.lg),
SectionLabel(text: l10n.settingsSectionNotification),
SettingsGroupCard(
children: [
SettingsSwitchTile(
icon: Icons.notifications_rounded,
title: l10n.settingsNotificationAllow,
value: _settings.notification.allowNotifications,
tint: colors.primary,
background: colors.surfaceContainerHighest,
onChanged: (value) =>
_updateNotification(allowNotifications: value),
),
SettingsSwitchTile(
icon: Icons.vibration_rounded,
title: l10n.settingsNotificationVibration,
value: _settings.notification.allowVibration,
tint: colors.primary,
background: colors.surfaceContainerHighest,
2026-04-03 16:56:47 +08:00
showDivider: false,
onChanged: (value) =>
_updateNotification(allowVibration: value),
2026-04-03 16:56:47 +08:00
),
],
),
],
),
),
);
}
Future<void> _selectLanguage(
String currentLanguage,
void Function(String) onChanged,
) async {
final result = await Navigator.of(context).push<String>(
MaterialPageRoute<String>(
builder: (_) =>
LanguageSettingsScreen(selectedLanguageTag: currentLanguage),
),
);
if (result == null || result == currentLanguage) {
return;
}
onChanged(result);
await widget.onSettingsChanged(_settings);
}
Future<void> _selectTimezone(BuildContext context) async {
2026-04-03 16:56:47 +08:00
final result = await Navigator.of(context).push<String>(
MaterialPageRoute<String>(
builder: (_) => TimezoneSettingsScreen(
selectedTimezone: _settings.preferences.timezone,
2026-04-03 16:56:47 +08:00
),
),
);
if (result == null || result == _settings.preferences.timezone) {
2026-04-03 16:56:47 +08:00
return;
}
setState(() {
_settings = _settings.copyWith(
preferences: _settings.preferences.copyWith(timezone: result),
);
});
await widget.onSettingsChanged(_settings);
}
Future<void> _selectCountry(BuildContext context) async {
final result = await Navigator.of(context).push<String>(
MaterialPageRoute<String>(
builder: (_) => CountrySettingsScreen(
selectedCountry: _settings.preferences.country,
),
),
);
if (result == null || result == _settings.preferences.country) {
2026-04-03 16:56:47 +08:00
return;
}
setState(() {
_settings = _settings.copyWith(
preferences: _settings.preferences.copyWith(country: result),
2026-04-03 16:56:47 +08:00
);
});
await widget.onSettingsChanged(_settings);
}
void _updateNotification({bool? allowNotifications, bool? allowVibration}) {
final newNotification = _settings.notification.copyWith(
allowNotifications: allowNotifications,
allowVibration: allowVibration,
);
final newSettings = _settings.copyWith(notification: newNotification);
setState(() {
_settings = newSettings;
});
widget.onSettingsChanged(newSettings);
}
}
class TimezoneSettingsScreen extends StatelessWidget {
const TimezoneSettingsScreen({super.key, required this.selectedTimezone});
final String selectedTimezone;
static const _timezones = [
'Asia/Shanghai',
'Asia/Hong_Kong',
'Asia/Tokyo',
'America/New_York',
'America/Los_Angeles',
'Europe/London',
'Europe/Paris',
];
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final colors = Theme.of(context).colorScheme;
return Scaffold(
backgroundColor: colors.surfaceContainerLow,
appBar: AppBar(
title: Text(l10n.settingsTimezone),
centerTitle: true,
backgroundColor: colors.surfaceContainerLow,
surfaceTintColor: colors.surfaceContainerLow,
),
body: ListView(
padding: const EdgeInsets.all(AppSpacing.lg),
children: [
SettingsGroupCard(
children: [
for (int i = 0; i < _timezones.length; i++)
SettingsMenuTile(
icon: Icons.access_time_rounded,
title: _timezones[i],
subtitle: '',
tint: colors.primary,
background: colors.surfaceContainerHighest,
showDivider: i != _timezones.length - 1,
showChevron: false,
trailing: selectedTimezone == _timezones[i]
? Icon(Icons.check_rounded, color: colors.primary)
: null,
onTap: () => Navigator.of(context).pop(_timezones[i]),
),
],
),
],
),
);
}
}
class CountrySettingsScreen extends StatelessWidget {
const CountrySettingsScreen({super.key, required this.selectedCountry});
final String selectedCountry;
static const _countries = ['CN', 'HK', 'TW', 'US', 'JP', 'GB', 'FR'];
static const _labels = [
'China',
'Hong Kong',
'Taiwan',
'USA',
'Japan',
'UK',
'France',
];
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final colors = Theme.of(context).colorScheme;
return Scaffold(
backgroundColor: colors.surfaceContainerLow,
appBar: AppBar(
title: Text(l10n.settingsCountry),
centerTitle: true,
backgroundColor: colors.surfaceContainerLow,
surfaceTintColor: colors.surfaceContainerLow,
),
body: ListView(
padding: const EdgeInsets.all(AppSpacing.lg),
children: [
SettingsGroupCard(
children: [
for (int i = 0; i < _countries.length; i++)
SettingsMenuTile(
icon: Icons.public_rounded,
title: _labels[i],
subtitle: _countries[i],
tint: colors.primary,
background: colors.surfaceContainerHighest,
showDivider: i != _countries.length - 1,
showChevron: false,
trailing: selectedCountry == _countries[i]
? Icon(Icons.check_rounded, color: colors.primary)
: null,
onTap: () => Navigator.of(context).pop(_countries[i]),
),
],
),
],
),
);
2026-04-03 16:56:47 +08:00
}
}