// 时间: 2026-04-02 // 功能: 主题控制器 - 管理深色模式和主题设置 // 介绍: 使用 GetX 管理主题状态,支持状态持久化到 SharedPreferences import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../models/night-mode/theme_model.dart'; /// 主题控制器 /// 管理应用的主题模式、深色模式状态,并持久化到 SharedPreferences class ThemeController extends GetxController { // SharedPreferences 实例 SharedPreferences? _prefs; // 存储键名 static const String _darkModeKey = 'darkMode'; static const String _themeModeKey = 'themeMode'; static const String _themeColorIndexKey = 'themeColorIndex'; static const String _accentColorIndexKey = 'accentColorIndex'; static const String _fontSizeIndexKey = 'fontSizeIndex'; static const String _enableAnimationKey = 'enableAnimation'; static const String _enableBlurEffectKey = 'enableBlurEffect'; // 可观察状态 final _isDarkMode = false.obs; final _themeMode = AppThemeMode.system.obs; final _themeColorIndex = 0.obs; final _accentColorIndex = 0.obs; final _fontSizeIndex = 1.obs; final _enableAnimation = true.obs; final _enableBlurEffect = true.obs; // Getters bool get isDarkMode => _isDarkMode.value; AppThemeMode get themeMode => _themeMode.value; int get themeColorIndex => _themeColorIndex.value; int get accentColorIndex => _accentColorIndex.value; int get fontSizeIndex => _fontSizeIndex.value; bool get enableAnimation => _enableAnimation.value; bool get enableBlurEffect => _enableBlurEffect.value; /// 获取当前 Flutter ThemeMode ThemeMode get currentThemeMode { if (_isDarkMode.value) { return ThemeMode.dark; } return _themeMode.value.themeMode; } /// 获取 Rx 状态(供 Obx 使用) RxBool get isDarkModeRx => _isDarkMode; Rx get themeModeRx => _themeMode; RxInt get themeColorIndexRx => _themeColorIndex; RxInt get accentColorIndexRx => _accentColorIndex; RxInt get fontSizeIndexRx => _fontSizeIndex; RxBool get enableAnimationRx => _enableAnimation; RxBool get enableBlurEffectRx => _enableBlurEffect; @override void onInit() { super.onInit(); _loadThemeSettings(); } /// SharedPreferences 实例(公开访问,供其他组件使用) SharedPreferences? get prefs => _prefs; /// 加载主题设置 Future _loadThemeSettings() async { _prefs = await SharedPreferences.getInstance(); _isDarkMode.value = _prefs?.getBool(_darkModeKey) ?? false; _themeMode.value = AppThemeMode.values[ (_prefs?.getInt(_themeModeKey) ?? 0).clamp(0, AppThemeMode.values.length - 1) ]; _themeColorIndex.value = _prefs?.getInt(_themeColorIndexKey) ?? 0; _accentColorIndex.value = _prefs?.getInt(_accentColorIndexKey) ?? 0; _fontSizeIndex.value = _prefs?.getInt(_fontSizeIndexKey) ?? 1; _enableAnimation.value = _prefs?.getBool(_enableAnimationKey) ?? true; _enableBlurEffect.value = _prefs?.getBool(_enableBlurEffectKey) ?? true; // 应用主题模式 _applyThemeMode(); } /// 保存主题设置 Future _saveThemeSettings() async { _prefs ??= await SharedPreferences.getInstance(); await _prefs?.setBool(_darkModeKey, _isDarkMode.value); await _prefs?.setInt(_themeModeKey, _themeMode.value.index); await _prefs?.setInt(_themeColorIndexKey, _themeColorIndex.value); await _prefs?.setInt(_accentColorIndexKey, _accentColorIndex.value); await _prefs?.setInt(_fontSizeIndexKey, _fontSizeIndex.value); await _prefs?.setBool(_enableAnimationKey, _enableAnimation.value); await _prefs?.setBool(_enableBlurEffectKey, _enableBlurEffect.value); } /// 应用主题模式到 GetX void _applyThemeMode() { Get.changeThemeMode(currentThemeMode); } /// 切换深色模式 /// [enabled] true 开启深色模式, false 关闭深色模式 Future toggleDarkMode(bool enabled) async { if (_isDarkMode.value == enabled) return; _isDarkMode.value = enabled; await _saveThemeSettings(); _applyThemeMode(); // 显示提示 Get.snackbar( '主题切换', enabled ? '已切换到深色模式 🌙' : '已切换到浅色模式 ☀️', snackPosition: SnackPosition.BOTTOM, duration: const Duration(seconds: 2), margin: const EdgeInsets.all(16), borderRadius: 12, ); } /// 设置主题模式 Future setThemeMode(AppThemeMode mode) async { if (_themeMode.value == mode) return; _themeMode.value = mode; await _saveThemeSettings(); _applyThemeMode(); } /// 切换主题模式(循环切换: system -> light -> dark -> system) Future cycleThemeMode() async { final currentIndex = _themeMode.value.index; final nextIndex = (currentIndex + 1) % AppThemeMode.values.length; await setThemeMode(AppThemeMode.values[nextIndex]); } /// 设置主题色索引 Future setThemeColorIndex(int index) async { if (_themeColorIndex.value == index) return; _themeColorIndex.value = index; await _saveThemeSettings(); } /// 设置强调色索引 Future setAccentColorIndex(int index) async { if (_accentColorIndex.value == index) return; _accentColorIndex.value = index; await _saveThemeSettings(); } /// 设置字体大小索引 Future setFontSizeIndex(int index) async { if (_fontSizeIndex.value == index) return; _fontSizeIndex.value = index; await _saveThemeSettings(); } /// 切换动画效果 Future toggleAnimation(bool enabled) async { if (_enableAnimation.value == enabled) return; _enableAnimation.value = enabled; await _saveThemeSettings(); } /// 切换模糊效果 Future toggleBlurEffect(bool enabled) async { if (_enableBlurEffect.value == enabled) return; _enableBlurEffect.value = enabled; await _saveThemeSettings(); } /// 获取主题配置 ThemeConfig get themeConfig { return ThemeConfig( isDarkMode: _isDarkMode.value, themeMode: _themeMode.value, themeColorIndex: _themeColorIndex.value, accentColorIndex: _accentColorIndex.value, fontSizeIndex: _fontSizeIndex.value, enableAnimation: _enableAnimation.value, enableBlurEffect: _enableBlurEffect.value, ); } /// 应用完整主题配置 Future applyThemeConfig(ThemeConfig config) async { _isDarkMode.value = config.isDarkMode; _themeMode.value = config.themeMode; _themeColorIndex.value = config.themeColorIndex; _accentColorIndex.value = config.accentColorIndex; _fontSizeIndex.value = config.fontSizeIndex; _enableAnimation.value = config.enableAnimation; _enableBlurEffect.value = config.enableBlurEffect; await _saveThemeSettings(); _applyThemeMode(); } /// 重置为默认主题设置 Future resetToDefault() async { _isDarkMode.value = false; _themeMode.value = AppThemeMode.system; _themeColorIndex.value = 0; _accentColorIndex.value = 0; _fontSizeIndex.value = 1; _enableAnimation.value = true; _enableBlurEffect.value = true; await _saveThemeSettings(); _applyThemeMode(); Get.snackbar( '主题重置', '已恢复默认主题设置', snackPosition: SnackPosition.BOTTOM, duration: const Duration(seconds: 2), margin: const EdgeInsets.all(16), borderRadius: 12, ); } /// 判断当前是否为深色模式(考虑系统设置) bool isDarkModeEffective(BuildContext context) { if (_isDarkMode.value) return true; if (_themeMode.value == AppThemeMode.dark) return true; if (_themeMode.value == AppThemeMode.system) { return MediaQuery.platformBrightnessOf(context) == Brightness.dark; } return false; } }