import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../constants/app_constants.dart'; import '../../../models/colors/theme_colors.dart'; import '../../../services/get/theme_controller.dart'; /// 时间: 2026-03-27 /// 功能: 主题个性化设置页面 /// 介绍: 允许用户自定义应用主题、颜色、字体大小等 class AppDiyPage extends StatefulWidget { const AppDiyPage({super.key}); @override State createState() => _AppDiyPageState(); } class _AppDiyPageState extends State { // GetX ThemeController late ThemeController _themeController; // 本地状态(不通过ThemeController管理的设置) bool _showGuideOnStartup = true; int _cardSizeIndex = 1; // 0: 小, 1: 中, 2: 大 bool _enableSystemNavigation = false; bool _showDevNotice = true; // 是否显示开发中提示对话框 // 滚动控制 final ScrollController _scrollController = ScrollController(); bool _isScrolling = true; late Timer _scrollTimer; // 主题颜色选项 final List _themeColors = ThemeColors.themeColors; // 强调色选项 final List _accentColors = ThemeColors.accentColors; // 字体大小选项 final List _fontSizes = ['小', '中', '大']; // 卡片大小选项 final List _cardSizes = ['小', '中', '大']; @override void initState() { super.initState(); // 获取 ThemeController _themeController = Get.find(); _loadLocalSettings(); _startScrolling(); // 延迟显示开发中提示对话框 WidgetsBinding.instance.addPostFrameCallback((_) { if (_showDevNotice) { _showDevNoticeDialog(); } }); } // 显示开发中提示对话框 void _showDevNoticeDialog() { bool doNotShowAgain = false; showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { final primaryColor = _themeController.currentThemeColor; final isDark = _themeController.isDarkMode; return StatefulBuilder( builder: (context, setState) { return AlertDialog( backgroundColor: isDark ? const Color(0xFF2A2A2A) : Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), title: Row( children: [ Icon(Icons.construction, color: primaryColor), const SizedBox(width: 8), Text( '开发中', style: TextStyle( color: isDark ? Colors.white : Colors.black, ), ), ], ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '个性化设置开发中', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, color: isDark ? Colors.white : Colors.black, ), ), const SizedBox(height: 12), Text( '• ✅ 深色模式(已支持)', style: TextStyle( color: isDark ? Colors.grey[300] : Colors.black87, ), ), Text( '• ✅ 主题色彩(已支持)', style: TextStyle( color: isDark ? Colors.grey[300] : Colors.black87, ), ), Text( '• ⏳ 其他设置仅当前页面生效', style: TextStyle( color: isDark ? Colors.grey[300] : Colors.black87, ), ), Text( '• ⏳ 后续版本将陆续支持', style: TextStyle( color: isDark ? Colors.grey[300] : Colors.black87, ), ), const SizedBox(height: 12), Text( '感谢您的耐心等待!', style: TextStyle( color: isDark ? Colors.grey[400] : Colors.grey[600], fontSize: 12, ), ), ], ), actions: [ Row( children: [ Checkbox( value: doNotShowAgain, onChanged: (value) { setState(() { doNotShowAgain = value ?? false; }); }, activeColor: primaryColor, ), Text( '不再提醒', style: TextStyle( color: isDark ? Colors.grey[300] : Colors.black87, fontSize: 14, ), ), const Spacer(), ElevatedButton( onPressed: () async { if (doNotShowAgain) { await _themeController.prefs?.setBool( 'showDevNotice', false, ); setState(() { _showDevNotice = false; }); } Navigator.of(context).pop(); }, style: ElevatedButton.styleFrom( backgroundColor: primaryColor, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), 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(); } } /// 加载本地设置(不通过ThemeController管理的设置) void _loadLocalSettings() async { // ThemeController 会自动加载主题相关设置 // 这里只加载本地独有的设置 setState(() { _showGuideOnStartup = _themeController.prefs?.getBool('showGuideOnStartup') ?? true; _cardSizeIndex = _themeController.prefs?.getInt('cardSizeIndex') ?? 1; _enableSystemNavigation = _themeController.prefs?.getBool('enableSystemNavigation') ?? false; _showDevNotice = _themeController.prefs?.getBool('showDevNotice') ?? true; }); } /// 保存本地设置 void _saveLocalSettings() async { await _themeController.prefs?.setBool( 'showGuideOnStartup', _showGuideOnStartup, ); await _themeController.prefs?.setInt('cardSizeIndex', _cardSizeIndex); await _themeController.prefs?.setBool( 'enableSystemNavigation', _enableSystemNavigation, ); } @override Widget build(BuildContext context) { // 使用 Obx 监听 ThemeController 的状态变化 return Obx(() { final isDark = _themeController.isDarkModeRx.value; final themeColorIdx = _themeController.themeColorIndexRx.value; return Scaffold( appBar: AppBar( title: Text( '个性化', style: TextStyle(color: _themeController.currentThemeColor), ), backgroundColor: isDark ? Colors.grey[900] : Colors.white, elevation: 0, actions: [ IconButton( icon: Icon( Icons.info_outline, color: _themeController.currentThemeColor, ), onPressed: () => _showDevNoticeDialog(), tooltip: '显示开发中提示', ), ], ), backgroundColor: isDark ? Colors.grey[900] : Colors.grey[50], body: ListView( padding: const EdgeInsets.all(16), children: [ // 主题模式 _buildSection('显示设置', isDark), _buildSwitchItem( '深色模式', _themeController.isDarkModeRx.value, (value) => _themeController.toggleDarkMode(value), icon: Icons.dark_mode, isDark: isDark, themeColorIdx: themeColorIdx, ), // 主题颜色 _buildSection('主题颜色', isDark), _buildColorSelector( '色彩', _themeColors, _themeController.themeColorIndexRx.value, (index) => _themeController.setThemeColorIndex(index), isDark: isDark, ), _buildColorSelector( '强调色', _accentColors, _themeController.accentColorIndexRx.value, (index) => _themeController.setAccentColorIndex(index), isDark: isDark, ), // 字体大小 _buildSection('字体设置', isDark), _buildOptionSelector( '字体大小', _fontSizes, _themeController.fontSizeIndexRx.value, (index) { if (index != null) { _themeController.setFontSizeIndex(index); } }, icon: Icons.font_download, isDark: isDark, themeColorIdx: themeColorIdx, ), // 卡片大小 _buildSection('界面设置', isDark), _buildOptionSelector( '卡片阴影', _cardSizes, _cardSizeIndex, (index) { if (index != null) { setState(() { _cardSizeIndex = index; }); _saveLocalSettings(); } }, icon: Icons.crop_square, isDark: isDark, themeColorIdx: themeColorIdx, ), // 启动显示 _buildSwitchItem( '启动时显示引导页', _showGuideOnStartup, (value) { setState(() { _showGuideOnStartup = value; }); _saveLocalSettings(); }, icon: Icons.info_outline, isDark: isDark, themeColorIdx: themeColorIdx, ), // 动画效果 _buildSwitchItem( '启用动画效果', _themeController.enableAnimationRx.value, (value) => _themeController.toggleAnimation(value), icon: Icons.animation, isDark: isDark, themeColorIdx: themeColorIdx, ), // 模糊效果 _buildSwitchItem( '启用模糊效果', _themeController.enableBlurEffectRx.value, (value) => _themeController.toggleBlurEffect(value), icon: Icons.blur_on, isDark: isDark, themeColorIdx: themeColorIdx, ), // 系统导航 _buildSwitchItem( '软件悬浮球', _enableSystemNavigation, (value) { setState(() { _enableSystemNavigation = value; }); _saveLocalSettings(); }, icon: Icons.navigation, isDark: isDark, themeColorIdx: themeColorIdx, ), _buildSwitchItem( '转场动画', _themeController.enableAnimationRx.value, (value) => _themeController.toggleAnimation(value), icon: Icons.track_changes, isDark: isDark, themeColorIdx: themeColorIdx, ), // 设计风格 _buildSection('设计风格', isDark), _buildDesignStyleCard(isDark, themeColorIdx), const SizedBox(height: 40), ], ), ); }); } Widget _buildSection(String title, bool isDark) { return Padding( padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8), child: Text( title, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: isDark ? Colors.grey[300] : Colors.grey[700], ), ), ); } Widget _buildSwitchItem( String title, bool value, ValueChanged onChanged, { required IconData icon, required bool isDark, required int themeColorIdx, }) { return Container( margin: const EdgeInsets.symmetric(vertical: 4), decoration: BoxDecoration( color: isDark ? 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: _themeController.currentThemeColor), title: Text( title, style: TextStyle(color: isDark ? Colors.white : Colors.black87), ), trailing: Switch( value: value, onChanged: onChanged, activeThumbColor: _themeController.currentThemeColor, ), ), ); } Widget _buildOptionSelector( String title, List options, int selectedIndex, ValueChanged onChanged, { required IconData icon, required bool isDark, required int themeColorIdx, }) { return Container( margin: const EdgeInsets.symmetric(vertical: 4), decoration: BoxDecoration( color: isDark ? 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: _themeController.currentThemeColor), title: Text( title, style: TextStyle(color: isDark ? Colors.white : Colors.black87), ), trailing: DropdownButton( value: selectedIndex, onChanged: (value) { if (value != null) { onChanged(value); } }, items: options.asMap().entries.map((entry) { return DropdownMenuItem( value: entry.key, child: Text( entry.value, style: TextStyle(color: isDark ? Colors.white : Colors.black87), ), ); }).toList(), dropdownColor: isDark ? Colors.grey[800] : Colors.white, style: TextStyle(color: isDark ? Colors.white : Colors.black87), ), ), ); } // 颜色名称映射 final Map _colorNames = { AppConstants.primaryColor: '紫韵', Colors.blue: '天蓝', Colors.green: '翠绿', Colors.orange: '橙光', Colors.red: '朱红', Colors.teal: '青碧', const Color(0xFF8B4513): '书褐', Colors.yellow: '明黄', Colors.pink: '桃粉', Colors.cyan: '湖青', Colors.purple: '罗兰', const Color(0xFFF5F5F0): '宣纸', }; Widget _buildColorSelector( String title, List colors, int selectedIndex, ValueChanged onChanged, { required bool isDark, }) { return Container( margin: const EdgeInsets.symmetric(vertical: 4), decoration: BoxDecoration( color: isDark ? 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( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 8), child: Text( title, style: TextStyle( color: isDark ? Colors.white : Colors.black87, fontSize: 16, fontWeight: FontWeight.w500, ), ), ), SizedBox( height: 80, child: ListView.builder( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 12), itemCount: colors.length, itemBuilder: (context, index) { final color = colors[index]; final colorName = _colorNames[color] ?? '色彩${index + 1}'; final isSelected = index == selectedIndex; return GestureDetector( onTap: () => onChanged(index), child: Container( width: 60, margin: const EdgeInsets.symmetric( horizontal: 4, vertical: 8, ), child: Column( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: color, shape: BoxShape.circle, border: isSelected ? Border.all( color: isDark ? Colors.white : Colors.black, width: 3, ) : Border.all( color: isDark ? Colors.grey[600]! : Colors.grey[300]!, width: 1, ), boxShadow: isSelected ? [ BoxShadow( color: color.withAlpha(128), blurRadius: 8, offset: const Offset(0, 2), ), ] : null, ), child: isSelected ? Icon( Icons.check, color: color.computeLuminance() > 0.5 ? Colors.black : Colors.white, size: 24, ) : null, ), const SizedBox(height: 4), Text( colorName, style: TextStyle( fontSize: 11, color: isSelected ? (isDark ? Colors.white : Colors.black) : (isDark ? Colors.grey[400] : Colors.grey[600]), fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), ); }, ), ), ], ), ); } Widget _buildDesignStyleCard(bool isDark, int themeColorIdx) { return Container( margin: const EdgeInsets.symmetric(vertical: 4), decoration: BoxDecoration( color: isDark ? 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: isDark ? Colors.teal[300] : Colors.teal[700], size: 20, ), ), const SizedBox(width: 12), Text( '设计样式&主题风格', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: isDark ? Colors.white : Colors.black87, ), ), ], ), ), const Divider(height: 1), Padding( padding: const EdgeInsets.all(16), child: Column( children: [ GestureDetector( onTap: _toggleScroll, child: SizedBox( height: 40, child: SingleChildScrollView( controller: _scrollController, scrollDirection: Axis.horizontal, child: Row( children: [ _buildStyleChip( 'Material 2', Icons.style, isMd: true, isDark: isDark, ), const SizedBox(width: 8), _buildStyleChip( 'Material 3', Icons.auto_awesome, isMd: true, isDark: isDark, ), const SizedBox(width: 8), _buildStyleChip( '透明毛玻璃', Icons.blur_on, isDark: isDark, ), const SizedBox(width: 8), _buildStyleChip( '沉浸式渐变色', Icons.color_lens, isDark: isDark, ), const SizedBox(width: 8), _buildStyleChip( '动态光感效果', Icons.lightbulb_outline, isDark: isDark, ), const SizedBox(width: 8), ], ), ), ), ), const SizedBox(height: 12), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( gradient: LinearGradient( colors: [ _themeController.currentThemeColor.withAlpha(10), _themeController.currentThemeColor.withAlpha(5), ], ), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Icon( Icons.lightbulb_outline, size: 16, color: _themeController.currentThemeColor, ), const SizedBox(width: 8), Expanded( child: Text( '采用现代Material Design设计语言,\n参考透明毛玻璃、沉浸式渐变色和动态光感等效果', style: TextStyle( fontSize: 12, color: isDark ? Colors.grey[300] : Colors.grey[700], ), ), ), ], ), ), ], ), ), ], ), ); } Widget _buildStyleChip( String label, IconData icon, { bool isMd = false, required bool isDark, }) { final color = isMd ? _themeController.currentThemeColor : isDark ? 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)), ], ), ); } }