610 lines
21 KiB
Dart
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('确定'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|