Files
kitchen/lib/src/pages/profile/settings/personalization_page.dart
2026-04-11 07:07:13 +08:00

610 lines
21 KiB
Dart

import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:mom_kitchen/src/controllers/user/personalization_controller.dart';
import 'package:mom_kitchen/src/services/core/app_service.dart';
import 'package:mom_kitchen/src/services/ui/theme_service.dart';
import 'package:mom_kitchen/src/widgets/skeleton_widgets.dart';
import 'package:mom_kitchen/src/widgets/states/standard_dialog.dart';
class PersonalizationPage extends StatelessWidget {
const PersonalizationPage({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<PersonalizationController>(
init: PersonalizationController(),
builder: (controller) {
final themeService = AppService.instance.theme;
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: const Text('个性化设置'),
previousPageTitle: '个人',
),
child: Obx(
() => SafeArea(
child: Container(
color: themeService.backgroundColor.value,
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 16),
children: [
CupertinoListSection.insetGrouped(
header: const Text('🎨 主题颜色'),
children: [
_buildColorPickerItem(controller, themeService),
],
),
CupertinoListSection.insetGrouped(
header: const Text('📝 字体大小'),
children: [_buildFontSizeItem(controller, themeService)],
),
CupertinoListSection.insetGrouped(
header: const Text('🌙 显示模式'),
children: [
CupertinoListTile(
title: const Text('跟随系统'),
trailing: CupertinoSwitch(
value:
themeService.darkModeSource.value ==
DarkModeSource.system,
onChanged: (v) {
controller.setDarkModeSource(
v
? DarkModeSource.system
: DarkModeSource.manual,
);
},
),
),
if (themeService.darkModeSource.value !=
DarkModeSource.system)
CupertinoListTile(
title: const Text('深色模式'),
trailing: CupertinoSwitch(
value: controller.isDarkMode,
onChanged: (_) => controller.toggleDarkMode(),
),
),
if (themeService.darkModeSource.value ==
DarkModeSource.system)
CupertinoListTile(
title: Text(
'当前: ${themeService.isDarkMode.value ? "深色模式 🌙" : "浅色模式 ☀️"}',
style: TextStyle(
fontSize: 14,
color: themeService.textColor.value.withValues(
alpha: 0.6,
),
),
),
),
],
),
CupertinoListSection.insetGrouped(
header: const Text('✨ 动画效果'),
children: [_buildAnimationItem(controller, themeService)],
),
CupertinoListSection.insetGrouped(
header: const Text('🌐 语言'),
children: _buildLanguageItems(controller, themeService),
),
CupertinoListSection.insetGrouped(
header: const Text('💬 对话框样式'),
children: [
..._buildDialogStyleItems(controller, themeService),
CupertinoListTile(
title: const Text('启用统一样式(跨平台一致)'),
trailing: CupertinoSwitch(
value: themeService.unifiedStyleEnabled.value,
onChanged: (v) => controller.setUnifiedStyle(v),
),
),
CupertinoListTile(
title: const Text('显示对话框样式示例'),
trailing: const CupertinoListTileChevron(),
onTap: () => _showDialogByStyle(themeService),
),
],
),
CupertinoListSection.insetGrouped(
header: const Text('💭 消息气泡样式'),
children: [
_buildMessageBubbleItem(controller, themeService),
],
),
CupertinoListSection.insetGrouped(
header: const Text('🔲 底部栏样式'),
children: [
_buildBottomBarItem(controller, themeService),
if (themeService.bottomBarStyle.value ==
BottomBarStyle.floating)
_buildBottomBarTransparencyItem(
controller,
themeService,
),
],
),
CupertinoListSection.insetGrouped(
header: const Text('🃏 卡片滑动方向'),
children: [
_buildCardScrollDirectionItem(controller, themeService),
],
),
CupertinoListSection.insetGrouped(
header: const Text('📱 状态栏'),
children: [
CupertinoListTile(
title: const Text('启用沉浸状态栏'),
trailing: CupertinoSwitch(
value: themeService.isStatusBarImmersive.value,
onChanged: (v) async {
await controller.setStatusBarImmersive(v);
},
),
),
],
),
CupertinoListSection.insetGrouped(
header: const Text('预览'),
children: [_buildPreviewItem(themeService)],
),
Padding(
padding: const EdgeInsets.all(16),
child: CupertinoButton.filled(
onPressed: () =>
_showResetDialog(controller, themeService),
child: const Text('恢复默认设置'),
),
),
const SizedBox(height: 20),
],
),
),
),
),
);
},
);
}
Widget _buildColorPickerItem(
PersonalizationController controller,
ThemeService themeService,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(controller.themePresets.length, (index) {
final color = controller.themePresetColors[index];
final isSelected =
themeService.primaryColor.value.toARGB32() ==
color.toARGB32();
return GestureDetector(
onTap: () => controller.setThemeColor(color),
child: Container(
width: 56,
height: 56,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: isSelected
? Border.all(
color: themeService.textColor.value,
width: 3,
)
: null,
boxShadow: [
if (isSelected)
BoxShadow(
color: color.withValues(alpha: 0.5),
blurRadius: 8,
spreadRadius: 2,
),
],
),
child: isSelected
? Icon(
CupertinoIcons.checkmark_alt,
color: themeService.backgroundColor.value,
size: 24,
)
: null,
),
);
}),
),
),
const SizedBox(height: 8),
Text(
controller.currentThemeColorName,
style: TextStyle(
fontSize: 13,
color: themeService.textColor.value.withValues(alpha: 0.6),
),
),
],
),
);
}
Widget _buildFontSizeItem(
PersonalizationController controller,
ThemeService themeService,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${controller.currentFontSize.toStringAsFixed(1)} pt',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: themeService.primaryColor.value,
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Text('A', style: TextStyle(fontSize: 12)),
Expanded(
child: CupertinoSlider(
value: controller.currentFontSize,
min: 12.0,
max: 24.0,
divisions: 12,
onChanged: (value) => controller.setFontSize(value),
),
),
const Text('A', style: TextStyle(fontSize: 20)),
],
),
],
),
);
}
Widget _buildAnimationItem(
PersonalizationController controller,
ThemeService themeService,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('动画强度'),
Text(
'${controller.currentAnimationIntensity.toStringAsFixed(1)}x',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: themeService.primaryColor.value,
),
),
],
),
const SizedBox(height: 8),
CupertinoSlider(
value: controller.currentAnimationIntensity,
min: 0.0,
max: 2.0,
divisions: 10,
onChanged: (value) => controller.setAnimationIntensity(value),
),
],
),
);
}
List<Widget> _buildLanguageItems(
PersonalizationController controller,
ThemeService themeService,
) {
return List.generate(controller.languageNames.length, (index) {
final languageName = controller.languageNames[index];
final languageCode = controller.languageCodes[index];
final isSelected = controller.currentLanguage == languageCode;
return CupertinoListTile(
title: Text(languageName),
trailing: isSelected
? Icon(
CupertinoIcons.checkmark_alt,
color: themeService.primaryColor.value,
)
: null,
onTap: () => controller.setLanguage(languageCode),
);
});
}
List<Widget> _buildDialogStyleItems(
PersonalizationController controller,
ThemeService themeService,
) {
return List.generate(controller.dialogStyleNames.length, (index) {
final name = controller.dialogStyleNames[index];
final style = DialogStyle.values[index];
final isSelected = themeService.dialogStyle.value == style;
return CupertinoListTile(
title: Text(name),
trailing: isSelected
? Icon(
CupertinoIcons.checkmark_alt,
color: themeService.primaryColor.value,
)
: null,
onTap: () => controller.setDialogStyle(style),
);
});
}
Widget _buildMessageBubbleItem(
PersonalizationController controller,
ThemeService themeService,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: CupertinoSlidingSegmentedControl<MessageBubbleStyle>(
groupValue: themeService.messageBubbleStyle.value,
onValueChanged: (v) {
if (v != null) controller.setMessageBubbleStyle(v);
},
children: {
for (int i = 0; i < controller.messageBubbleStyleNames.length; i++)
MessageBubbleStyle.values[i]: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Text(
controller.messageBubbleStyleNames[i],
style: const TextStyle(fontSize: 13),
),
),
},
),
);
}
Widget _buildBottomBarItem(
PersonalizationController controller,
ThemeService themeService,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: CupertinoSlidingSegmentedControl<BottomBarStyle>(
groupValue: themeService.bottomBarStyle.value,
onValueChanged: (v) {
if (v != null) controller.setBottomBarStyle(v);
},
children: {
for (int i = 0; i < controller.bottomBarStyleNames.length; i++)
BottomBarStyle.values[i]: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Text(
controller.bottomBarStyleNames[i],
style: const TextStyle(fontSize: 13),
),
),
},
),
);
}
Widget _buildBottomBarTransparencyItem(
PersonalizationController controller,
ThemeService themeService,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('悬浮栏透明度'),
Text(
'${(controller.currentBottomBarTransparency * 100).round()}%',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: themeService.primaryColor.value,
),
),
],
),
const SizedBox(height: 8),
CupertinoSlider(
value: controller.currentBottomBarTransparency,
min: 0.0,
max: 1.0,
divisions: 20,
onChanged: (v) => controller.setBottomBarTransparency(v),
),
],
),
);
}
Widget _buildCardScrollDirectionItem(
PersonalizationController controller,
ThemeService themeService,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: CupertinoSlidingSegmentedControl<CardScrollDirection>(
groupValue: themeService.cardScrollDirection.value,
onValueChanged: (v) {
if (v != null) themeService.setCardScrollDirection(v);
},
children: const {
CardScrollDirection.horizontal: Padding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Text('↔️ 左右滑动', style: TextStyle(fontSize: 13)),
),
CardScrollDirection.vertical: Padding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Text('↕️ 上下滑动', style: TextStyle(fontSize: 13)),
),
},
),
);
}
Widget _buildPreviewItem(ThemeService themeService) {
return Padding(
padding: const EdgeInsets.all(16),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: themeService.backgroundColor.value.withValues(alpha: 0.02),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: themeService.textColor.value.withValues(alpha: 0.04),
),
),
child: MessagePreview(
style: themeService.messageBubbleStyle.value,
theme: themeService,
),
),
);
}
void _showDialogByStyle(ThemeService themeService) {
final style = themeService.dialogStyle.value;
const title = '示例对话框';
const content = '这是使用当前对话框样式的演示。';
if (themeService.unifiedStyleEnabled.value) {
StandardDialog.show(
Get.context!,
title: title,
message: content,
confirmText: '确定',
cancelText: '取消',
);
return;
}
switch (style) {
case DialogStyle.native:
showCupertinoDialog(
context: Get.context!,
builder: (ctx) => CupertinoAlertDialog(
title: const Text(title),
content: const Text(content),
actions: [
CupertinoDialogAction(
onPressed: () => Get.back(),
child: const Text('取消'),
),
CupertinoDialogAction(
isDestructiveAction: true,
onPressed: () => Get.back(),
child: const Text('确定'),
),
],
),
);
break;
case DialogStyle.toast:
Get.snackbar(title, content, snackPosition: SnackPosition.BOTTOM);
break;
case DialogStyle.hybrid:
Get.dialog(
Center(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: themeService.backgroundColor.value,
borderRadius: BorderRadius.circular(12),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
style: TextStyle(fontSize: themeService.fontSize.value + 2),
),
const SizedBox(height: 8),
Text(
content,
style: TextStyle(fontSize: themeService.fontSize.value),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: CupertinoButton(
onPressed: () => Get.back(),
child: const Text('取消'),
),
),
Expanded(
child: CupertinoButton.filled(
onPressed: () => Get.back(),
child: const Text('确定'),
),
),
],
),
],
),
),
),
);
break;
case DialogStyle.getx:
Get.defaultDialog(
title: title,
middleText: content,
textCancel: '取消',
textConfirm: '确定',
);
break;
}
}
void _showResetDialog(
PersonalizationController controller,
ThemeService themeService,
) {
showCupertinoDialog(
context: Get.context!,
builder: (context) => CupertinoAlertDialog(
title: const Text('恢复默认设置'),
content: const Text('确定要恢复所有设置到默认值吗?'),
actions: [
CupertinoDialogAction(
onPressed: () => Get.back(),
child: const Text('取消'),
),
CupertinoDialogAction(
isDestructiveAction: true,
onPressed: () {
Get.back();
controller.resetToDefaults();
},
child: const Text('确定'),
),
],
),
);
}
}