Files
wushu/lib/views/profile/theme/app-diy.dart
2026-03-31 21:59:07 +08:00

586 lines
18 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../constants/app_constants.dart';
/// 时间: 2026-03-27
/// 功能: 主题个性化设置页面
/// 介绍: 允许用户自定义应用主题、颜色、字体大小等
class AppDiyPage extends StatefulWidget {
const AppDiyPage({super.key});
@override
State<AppDiyPage> createState() => _AppDiyPageState();
}
class _AppDiyPageState extends State<AppDiyPage> {
// 主题设置
bool _isDarkMode = false;
int _themeColorIndex = 0;
int _fontSizeIndex = 1; // 0: 小, 1: 中, 2: 大
bool _showGuideOnStartup = true;
int _cardSizeIndex = 1; // 0: 小, 1: 中, 2: 大
bool _enableAnimation = true;
bool _enableBlurEffect = true;
bool _enableSystemNavigation = false;
int _accentColorIndex = 0;
// 滚动控制
final ScrollController _scrollController = ScrollController();
bool _isScrolling = true;
late Timer _scrollTimer;
// 主题颜色选项
final List<Color> _themeColors = [
AppConstants.primaryColor, // 默认紫色
Colors.blue, // 蓝色
Colors.green, // 绿色
Colors.orange, // 橙色
Colors.red, // 红色
Colors.teal, // 青色
];
// 强调色选项
final List<Color> _accentColors = [
AppConstants.primaryColor, // 默认紫色
Colors.yellow, // 黄色
Colors.pink, // 粉色
Colors.cyan, // 青色
Colors.purple, // 深紫色
];
// 字体大小选项
final List<String> _fontSizes = ['', '', ''];
// 卡片大小选项
final List<String> _cardSizes = ['', '', ''];
@override
void initState() {
super.initState();
_loadSettings();
_startScrolling();
// 延迟显示开发中提示对话框
WidgetsBinding.instance.addPostFrameCallback((_) {
_showDevNoticeDialog();
});
}
// 显示开发中提示对话框
void _showDevNoticeDialog() {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Row(
children: [
Icon(Icons.construction, color: AppConstants.primaryColor),
const SizedBox(width: 8),
const Text('开发中'),
],
),
content: const Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'个性化设置开发中',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
SizedBox(height: 12),
Text('• 当前设置仅当前页面生效'),
Text('• 后续版本将陆续支持'),
Text('• 可提前预览主题风格设置'),
SizedBox(height: 12),
Text(
'感谢您的耐心等待!',
style: TextStyle(color: Colors.grey, fontSize: 12),
),
],
),
actions: [
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.primaryColor,
foregroundColor: Colors.white,
),
child: const Text('我知道了'),
),
],
);
},
);
}
@override
void dispose() {
_scrollController.dispose();
_scrollTimer.cancel();
super.dispose();
}
void _startScrolling() {
_isScrolling = true;
_scrollTimer = Timer.periodic(const Duration(milliseconds: 30), (timer) {
if (_scrollController.hasClients) {
_scrollController.jumpTo(_scrollController.offset + 1);
// 当滚动到末尾时,重新开始滚动
if (_scrollController.offset >=
_scrollController.position.maxScrollExtent) {
_scrollController.jumpTo(0);
}
}
});
}
void _stopScrolling() {
_isScrolling = false;
_scrollTimer.cancel();
}
void _toggleScroll() {
if (_isScrolling) {
_stopScrolling();
} else {
_startScrolling();
}
}
void _loadSettings() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_isDarkMode = prefs.getBool('darkMode') ?? false;
_themeColorIndex = prefs.getInt('themeColorIndex') ?? 0;
_fontSizeIndex = prefs.getInt('fontSizeIndex') ?? 1;
_showGuideOnStartup = prefs.getBool('showGuideOnStartup') ?? true;
_cardSizeIndex = prefs.getInt('cardSizeIndex') ?? 1;
_enableAnimation = prefs.getBool('enableAnimation') ?? true;
_enableBlurEffect = prefs.getBool('enableBlurEffect') ?? true;
_enableSystemNavigation =
prefs.getBool('enableSystemNavigation') ?? false;
_accentColorIndex = prefs.getInt('accentColorIndex') ?? 0;
});
}
void _saveSettings() async {
final prefs = await SharedPreferences.getInstance();
prefs.setBool('darkMode', _isDarkMode);
prefs.setInt('themeColorIndex', _themeColorIndex);
prefs.setInt('fontSizeIndex', _fontSizeIndex);
prefs.setBool('showGuideOnStartup', _showGuideOnStartup);
prefs.setInt('cardSizeIndex', _cardSizeIndex);
prefs.setBool('enableAnimation', _enableAnimation);
prefs.setBool('enableBlurEffect', _enableBlurEffect);
prefs.setBool('enableSystemNavigation', _enableSystemNavigation);
prefs.setInt('accentColorIndex', _accentColorIndex);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('个性化'),
backgroundColor: _isDarkMode ? Colors.grey[900] : Colors.white,
foregroundColor: _isDarkMode ? Colors.white : AppConstants.primaryColor,
elevation: 0,
),
backgroundColor: _isDarkMode ? Colors.grey[900] : Colors.grey[50],
body: ListView(
padding: const EdgeInsets.all(16),
children: [
// 主题模式
_buildSection('显示设置'),
_buildSwitchItem('深色模式', _isDarkMode, (value) {
setState(() {
_isDarkMode = value;
_saveSettings();
});
}, icon: Icons.dark_mode),
// 主题颜色
_buildSection('主题颜色'),
_buildColorSelector('色彩', _themeColors, _themeColorIndex, (index) {
setState(() {
_themeColorIndex = index;
_saveSettings();
});
}),
_buildColorSelector('强调色', _accentColors, _accentColorIndex, (index) {
setState(() {
_accentColorIndex = index;
_saveSettings();
});
}),
// 字体大小
_buildSection('字体设置'),
_buildOptionSelector('字体大小', _fontSizes, _fontSizeIndex, (index) {
if (index != null) {
setState(() {
_fontSizeIndex = index;
_saveSettings();
});
}
}, icon: Icons.font_download),
// 卡片大小
_buildSection('界面设置'),
_buildOptionSelector('卡片阴影', _cardSizes, _cardSizeIndex, (index) {
if (index != null) {
setState(() {
_cardSizeIndex = index;
_saveSettings();
});
}
}, icon: Icons.crop_square),
// 启动显示
_buildSwitchItem('启动时显示引导页', _showGuideOnStartup, (value) {
setState(() {
_showGuideOnStartup = value;
_saveSettings();
});
}, icon: Icons.info_outline),
// 动画效果
_buildSwitchItem('启用动画效果', _enableAnimation, (value) {
setState(() {
_enableAnimation = value;
_saveSettings();
});
}, icon: Icons.animation),
// 模糊效果
_buildSwitchItem('启用模糊效果', _enableBlurEffect, (value) {
setState(() {
_enableBlurEffect = value;
_saveSettings();
});
}, icon: Icons.blur_on),
// 系统导航
_buildSwitchItem('软件悬浮球', _enableSystemNavigation, (value) {
setState(() {
_enableSystemNavigation = value;
_saveSettings();
});
}, icon: Icons.navigation),
_buildSwitchItem('转场动画', _enableAnimation, (value) {
setState(() {
// _enableAnimation = value;
_saveSettings();
});
}, icon: Icons.track_changes),
// 设计风格
_buildSection('设计风格'),
_buildDesignStyleCard(),
const SizedBox(height: 40),
],
),
);
}
Widget _buildSection(String title) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8),
child: Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _isDarkMode ? Colors.grey[300] : Colors.grey[700],
),
),
);
}
Widget _buildSwitchItem(
String title,
bool value,
ValueChanged<bool> onChanged, {
required IconData icon,
}) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 4),
decoration: BoxDecoration(
color: _isDarkMode ? Colors.grey[800] : Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(5),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
child: ListTile(
leading: Icon(icon, color: _themeColors[_themeColorIndex]),
title: Text(
title,
style: TextStyle(color: _isDarkMode ? Colors.white : Colors.black87),
),
trailing: Switch(
value: value,
onChanged: onChanged,
activeThumbColor: _themeColors[_themeColorIndex],
),
),
);
}
Widget _buildOptionSelector(
String title,
List<String> options,
int selectedIndex,
ValueChanged<int?> onChanged, {
required IconData icon,
}) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 4),
decoration: BoxDecoration(
color: _isDarkMode ? Colors.grey[800] : Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(5),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
child: ListTile(
leading: Icon(icon, color: _themeColors[_themeColorIndex]),
title: Text(
title,
style: TextStyle(color: _isDarkMode ? Colors.white : Colors.black87),
),
trailing: DropdownButton<int>(
value: selectedIndex,
onChanged: (value) {
if (value != null) {
onChanged(value);
}
},
items: options.asMap().entries.map((entry) {
return DropdownMenuItem<int>(
value: entry.key,
child: Text(
entry.value,
style: TextStyle(
color: _isDarkMode ? Colors.white : Colors.black87,
),
),
);
}).toList(),
dropdownColor: _isDarkMode ? Colors.grey[800] : Colors.white,
style: TextStyle(color: _isDarkMode ? Colors.white : Colors.black87),
),
),
);
}
Widget _buildColorSelector(
String title,
List<Color> colors,
int selectedIndex,
ValueChanged<int> onChanged,
) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 4),
decoration: BoxDecoration(
color: _isDarkMode ? Colors.grey[800] : Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(5),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
child: ListTile(
title: Text(
title,
style: TextStyle(color: _isDarkMode ? Colors.white : Colors.black87),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: colors.asMap().entries.map((entry) {
return GestureDetector(
onTap: () => onChanged(entry.key),
child: Container(
width: 32,
height: 32,
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
color: entry.value,
shape: BoxShape.circle,
border: entry.key == selectedIndex
? Border.all(
color: _isDarkMode ? Colors.white : Colors.black,
width: 2,
)
: null,
),
),
);
}).toList(),
),
),
);
}
Widget _buildDesignStyleCard() {
return Container(
margin: const EdgeInsets.symmetric(vertical: 4),
decoration: BoxDecoration(
color: _isDarkMode ? Colors.grey[800] : Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(5),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.teal.withAlpha(10),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.design_services,
color: _isDarkMode ? Colors.teal[300] : Colors.teal[700],
size: 20,
),
),
const SizedBox(width: 12),
Text(
'设计样式&主题风格',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _isDarkMode ? Colors.white : Colors.black87,
),
),
],
),
),
const Divider(height: 1),
Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
GestureDetector(
onTap: _toggleScroll,
child: Container(
height: 40,
child: SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.horizontal,
child: Row(
children: [
_buildStyleChip(
'Material 2',
Icons.style,
isMd: true,
),
const SizedBox(width: 8),
_buildStyleChip(
'Material 3',
Icons.auto_awesome,
isMd: true,
),
const SizedBox(width: 8),
_buildStyleChip('透明毛玻璃', Icons.blur_on),
const SizedBox(width: 8),
_buildStyleChip('沉浸式渐变色', Icons.color_lens),
const SizedBox(width: 8),
_buildStyleChip('动态光感效果', Icons.lightbulb_outline),
const SizedBox(width: 8),
],
),
),
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
_themeColors[_themeColorIndex].withAlpha(10),
_themeColors[_themeColorIndex].withAlpha(5),
],
),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Icon(
Icons.lightbulb_outline,
size: 16,
color: _themeColors[_themeColorIndex],
),
const SizedBox(width: 8),
Expanded(
child: Text(
'采用现代Material Design设计语言\n参考透明毛玻璃、沉浸式渐变色和动态光感等效果',
style: TextStyle(
fontSize: 12,
color: _isDarkMode
? Colors.grey[300]
: Colors.grey[700],
),
),
),
],
),
),
],
),
),
],
),
);
}
Widget _buildStyleChip(String label, IconData icon, {bool isMd = false}) {
final color = isMd
? _themeColors[_themeColorIndex]
: _isDarkMode
? Colors.blue[400]!
: Colors.blue[600]!;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: color.withAlpha(10),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: color.withAlpha(50), width: 1),
),
child: Row(
children: [
Icon(icon, size: 14, color: color),
const SizedBox(width: 4),
Text(label, style: TextStyle(fontSize: 12, color: color)),
],
),
);
}
}