241 lines
7.7 KiB
Dart
241 lines
7.7 KiB
Dart
// 时间: 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<AppThemeMode> 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<void> _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<void> _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<void> 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<void> setThemeMode(AppThemeMode mode) async {
|
||
if (_themeMode.value == mode) return;
|
||
|
||
_themeMode.value = mode;
|
||
await _saveThemeSettings();
|
||
_applyThemeMode();
|
||
}
|
||
|
||
/// 切换主题模式(循环切换: system -> light -> dark -> system)
|
||
Future<void> cycleThemeMode() async {
|
||
final currentIndex = _themeMode.value.index;
|
||
final nextIndex = (currentIndex + 1) % AppThemeMode.values.length;
|
||
await setThemeMode(AppThemeMode.values[nextIndex]);
|
||
}
|
||
|
||
/// 设置主题色索引
|
||
Future<void> setThemeColorIndex(int index) async {
|
||
if (_themeColorIndex.value == index) return;
|
||
|
||
_themeColorIndex.value = index;
|
||
await _saveThemeSettings();
|
||
}
|
||
|
||
/// 设置强调色索引
|
||
Future<void> setAccentColorIndex(int index) async {
|
||
if (_accentColorIndex.value == index) return;
|
||
|
||
_accentColorIndex.value = index;
|
||
await _saveThemeSettings();
|
||
}
|
||
|
||
/// 设置字体大小索引
|
||
Future<void> setFontSizeIndex(int index) async {
|
||
if (_fontSizeIndex.value == index) return;
|
||
|
||
_fontSizeIndex.value = index;
|
||
await _saveThemeSettings();
|
||
}
|
||
|
||
/// 切换动画效果
|
||
Future<void> toggleAnimation(bool enabled) async {
|
||
if (_enableAnimation.value == enabled) return;
|
||
|
||
_enableAnimation.value = enabled;
|
||
await _saveThemeSettings();
|
||
}
|
||
|
||
/// 切换模糊效果
|
||
Future<void> 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<void> 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<void> 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;
|
||
}
|
||
}
|