checkpoint: focus seed 1.0.1 localized dev switch

This commit is contained in:
2026-06-11 23:35:49 +08:00
parent c51abdfb73
commit d06dc35ee6
72 changed files with 6155 additions and 1390 deletions
+212
View File
@@ -0,0 +1,212 @@
import 'package:flutter/material.dart';
import '../../application/focus_seed_controller.dart';
import '../../l10n/l10n.dart';
import '../../models/appearance_preference.dart';
import '../../models/language_preference.dart';
import '../../widgets/dialogs.dart';
import '../../widgets/focus_seed_header.dart';
import '../../widgets/settings_tile.dart';
import '../../widgets/surface_panel.dart';
class MeScreen extends StatelessWidget {
const MeScreen({
required this.controller,
required this.appearance,
required this.onAppearanceChanged,
required this.language,
required this.showLanguageTools,
required this.onLanguageChanged,
super.key,
});
final FocusSeedController controller;
final AppearancePreference appearance;
final ValueChanged<AppearancePreference> onAppearanceChanged;
final LanguagePreference language;
final bool showLanguageTools;
final ValueChanged<LanguagePreference> onLanguageChanged;
@override
Widget build(BuildContext context) {
final state = controller.state!;
final l10n = context.l10n;
return Column(
children: [
FocusSeedHeader(
title: l10n.meTitle,
subtitle: l10n.meSubtitle,
isSaving: controller.isSaving,
),
Expanded(
child: ListView(
padding: const EdgeInsets.fromLTRB(20, 6, 20, 32),
children: [
Text(
l10n.appearance,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
SurfacePanel(
child: SizedBox(
width: double.infinity,
child: SegmentedButton<AppearancePreference>(
showSelectedIcon: false,
segments: [
ButtonSegment(
value: AppearancePreference.system,
icon: const Icon(Icons.brightness_auto_outlined),
label: Text(l10n.system),
),
ButtonSegment(
value: AppearancePreference.light,
icon: const Icon(Icons.light_mode_outlined),
label: Text(l10n.light),
),
ButtonSegment(
value: AppearancePreference.dark,
icon: const Icon(Icons.dark_mode_outlined),
label: Text(l10n.dark),
),
],
selected: {appearance},
onSelectionChanged:
(selection) => onAppearanceChanged(selection.first),
),
),
),
if (showLanguageTools) ...[
const SizedBox(height: 18),
_SectionHeader(
title: l10n.language,
detail: l10n.developmentOnly,
),
const SizedBox(height: 8),
SurfacePanel(
child: SizedBox(
width: double.infinity,
child: SegmentedButton<LanguagePreference>(
showSelectedIcon: false,
segments: [
ButtonSegment(
value: LanguagePreference.defaultLocale,
icon: const Icon(Icons.tune_rounded),
label: Text(l10n.languageDefault),
),
ButtonSegment(
value: LanguagePreference.english,
icon: const Icon(Icons.language_rounded),
label: Text(l10n.languageEnglish),
),
ButtonSegment(
value: LanguagePreference.chinese,
icon: const Icon(Icons.translate_rounded),
label: Text(l10n.languageChinese),
),
],
selected: {language},
onSelectionChanged:
(selection) => onLanguageChanged(selection.first),
),
),
),
],
const SizedBox(height: 18),
Text(
l10n.yourData,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
SurfacePanel(
padding: const EdgeInsets.symmetric(horizontal: 14),
child: Column(
children: [
SettingsTile(
icon: Icons.ios_share_outlined,
title: l10n.exportJson,
subtitle: l10n.exportJsonSubtitle,
onTap: () => showExportDialog(context, state),
),
const Divider(),
SettingsTile(
icon: Icons.file_upload_outlined,
title: l10n.importJson,
subtitle: l10n.importJsonSubtitle,
onTap:
() => showImportDialog(context, (raw) async {
final imported = await controller.importState(raw);
if (context.mounted) {
showSnack(
context,
imported
? l10n.dataImported
: l10n.importFailed,
);
}
}),
),
const Divider(),
SettingsTile(
icon: Icons.privacy_tip_outlined,
title: l10n.privacyTitle,
subtitle: l10n.privacySubtitle,
onTap: () => showPrivacySheet(context),
),
],
),
),
const SizedBox(height: 12),
SurfacePanel(
padding: const EdgeInsets.symmetric(horizontal: 14),
child: SettingsTile(
icon: Icons.delete_outline,
title: l10n.resetLocalGarden,
subtitle: l10n.resetLocalGardenSubtitle,
destructive: true,
onTap:
() => confirmClearData(context, () async {
final cleared = await controller.clearData();
if (context.mounted && cleared) {
showSnack(context, l10n.localDataCleared);
}
}),
),
),
const SizedBox(height: 18),
Text(
'Focus Seed 1.0.1',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
],
),
),
],
);
}
}
class _SectionHeader extends StatelessWidget {
const _SectionHeader({required this.title, required this.detail});
final String title;
final String detail;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Row(
children: [
Expanded(child: Text(title, style: theme.textTheme.titleMedium)),
Text(
detail,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
);
}
}