深色模式、首页设置页面和功能优化

This commit is contained in:
Developer
2026-04-02 07:06:55 +08:00
parent f0a62ed68b
commit 954d173329
88 changed files with 12157 additions and 7578 deletions

View File

@@ -0,0 +1,110 @@
import 'package:get/get.dart';
import 'package:flutter/material.dart';
/// 时间: 2026-04-01
/// 功能: 分类页面控制器,管理分类数据和状态
/// 介绍: 负责管理分类页面的标签切换、分类数据和导航逻辑
class CategoryController extends GetxController
with GetSingleTickerProviderStateMixin {
// Tab控制器
late TabController tabController;
// 标签分类
final tabCategories = [
{'label': '场景分类', 'icon': Icons.category},
{'label': '朝代分类', 'icon': Icons.history},
];
// 场景分类数据
final sceneData = {
"节日": ["七夕节", "中秋节", "元宵节", "寒食节", "清明节", "端午节", "重阳节", "春节", "节日"],
"季节": ["三月", "二月", "冬天", "夏天", "春天", "春季", "秋天"],
"古籍": [
"三国志",
"三国演义",
"三字经",
"中庸",
"列子",
"史记",
"后汉书",
"吕氏春秋",
"商君书",
"围炉夜话",
"增广贤文",
"墨子",
"孙子兵法",
"孟子",
"小窗幽记",
"尚书",
"左传",
"幼学琼林",
"庄子",
"战国策",
"文心雕龙",
"易传",
"晋书",
"汉书",
"淮南子",
"礼记",
"管子",
"红楼梦",
"老子",
"荀子",
"菜根谭",
"警世通言",
"论语",
"资治通鉴",
"韩非子",
"鬼谷子",
"古籍",
"格言联璧",
],
"情感": ["伤感", "励志", "友情", "思乡", "思念", "感恩", "爱国", "爱情", "离别"],
"景物": ["庐山", "泰山", "西湖", "长江", "黄河", "边塞", "田园", "山水", "夜景"],
"天文气象": ["写云", "写雨", "写雪", "写风", "星星", "月亮", "流星"],
"动植物": ["写鸟", "柳树", "桃花", "梅花", "竹子", "荷花", "菊花"],
"语言文学": ["对联", "谚语", "一言", "读书", "哲理"],
"其他": ["母亲", "老师", "户外", "礼物", ""],
};
// 朝代分类数据
final dynastyData = {
"主要朝代": ["唐代", "宋代", "元代", "明代", "清代"],
"古代朝代": ["南北朝", "五代", "隋代"],
"近现代": ["近现代", "用户投稿", "管理员测试"],
"其他": ["暂无朝代"],
};
@override
void onInit() {
super.onInit();
// 初始化Tab控制器
tabController = TabController(length: tabCategories.length, vsync: this);
}
@override
void onClose() {
// 释放Tab控制器
tabController.dispose();
super.onClose();
}
// 获取当前分类类型
String getCurrentCategoryType() {
return tabCategories[tabController.index]['label'] as String;
}
// 获取当前分类数据
Map<String, List<String>> getCurrentCategoryData() {
return tabController.index == 0 ? sceneData : dynastyData;
}
// 导航到分类详情页
void navigateToCategoryDetail(String label, String categoryType) {
final searchType = categoryType == '朝代分类' ? 'alias' : 'keywords';
Get.toNamed(
'/corrPage',
arguments: {'label': label, 'searchType': searchType},
);
}
}

View File

@@ -0,0 +1,143 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../controllers/shared_preferences_storage_controller.dart';
import '../../constants/app_constants.dart';
class DiscoverController extends GetxController {
var categories = <String>[].obs;
var showTips = true.obs;
var globalTipsEnabled = true.obs;
var isDeveloperMode = false.obs;
var isInitialized = false.obs;
var currentTabIndex = 0.obs;
@override
void onInit() {
super.onInit();
// 立即初始化分类,不等待异步操作
updateCategories();
// 异步加载开发者模式设置
loadDeveloperMode();
// 异步加载全局Tips开关设置
loadGlobalTipsSetting();
}
Future<void> loadGlobalTipsSetting() async {
try {
final isEnabled = await SharedPreferencesStorageController.getBool(
'global_tips_enabled',
defaultValue: true,
);
globalTipsEnabled.value = isEnabled;
// 如果全局开关关闭本地showTips也设置为false
if (!isEnabled) {
showTips.value = false;
}
} catch (e) {
// 忽略错误,使用默认值
globalTipsEnabled.value = true;
}
}
Future<void> loadDeveloperMode() async {
try {
final isEnabled = await SharedPreferencesStorageController.getBool(
'developer_mode_enabled',
defaultValue: false,
);
isDeveloperMode.value = isEnabled;
// 更新分类(如果开发者模式状态改变)
updateCategories();
} catch (e) {
// 忽略错误,使用默认值
isDeveloperMode.value = false;
}
}
void updateCategories() {
categories.value = ['分类', '热门', '搜索'];
if (isDeveloperMode.value) {
categories.value.add('活跃');
}
isInitialized.value = true;
}
Future<void> refreshContent() async {
await Future.delayed(const Duration(seconds: 1));
Get.snackbar('提示', '内容已刷新');
}
void toggleTips() {
showTips.value = false;
}
void likeContent(int index) {
Get.snackbar('提示', '点赞了内容${index + 1}');
}
void commentContent(int index) {
Get.snackbar('提示', '评论了内容${index + 1}');
}
void shareContent(int index) {
Get.snackbar('提示', '分享了内容${index + 1}');
}
void bookmarkContent(int index) {
Get.snackbar('提示', '收藏了内容${index + 1}');
}
void showContentDetails(BuildContext context, int index, String category) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('$category - 内容${index + 1}'),
content: Text('这是$category分类下内容${index + 1}的详细信息。'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('关闭'),
),
],
),
);
}
void showMoreOptions(BuildContext context, int index) {
showModalBottomSheet(
context: context,
builder: (context) => Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.report),
title: const Text('举报'),
onTap: () {
Navigator.pop(context);
Get.snackbar('提示', '举报内容');
},
),
ListTile(
leading: const Icon(Icons.block),
title: const Text('屏蔽用户'),
onTap: () {
Navigator.pop(context);
Get.snackbar('提示', '屏蔽用户');
},
),
ListTile(
leading: const Icon(Icons.link),
title: const Text('复制链接'),
onTap: () {
Navigator.pop(context);
Get.snackbar('提示', '链接已复制');
},
),
],
),
),
);
}
}

View File

@@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constants/app_constants.dart';
class FavoritesController extends GetxController {
var categories = ['全部', '点赞', '笔记', '推送', '每日一句'].obs;
var isGridView = true.obs;
var currentTabIndex = 0.obs;
var searchQuery = ''.obs;
void toggleViewMode() {
isGridView.value = !isGridView.value;
}
void setCurrentTabIndex(int index) {
currentTabIndex.value = index;
}
void setSearchQuery(String query) {
searchQuery.value = query;
}
Future<void> refreshContent() async {
await Future.delayed(const Duration(milliseconds: 500));
Get.snackbar('提示', '内容已刷新');
}
void showFilterOptions(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (context) => Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'筛选选项',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
ListTile(
leading: const Icon(Icons.date_range),
title: const Text('按时间排序'),
onTap: () {
Navigator.pop(context);
Get.snackbar('提示', '按时间排序');
},
),
ListTile(
leading: const Icon(Icons.title),
title: const Text('按标题排序'),
onTap: () {
Navigator.pop(context);
Get.snackbar('提示', '按标题排序');
},
),
],
),
),
);
}
void navigateToSearch() {
Get.toNamed('/search', arguments: searchQuery.value.isEmpty ? null : searchQuery.value);
}
void navigateToCollectNotes() {
Get.toNamed('/collect-notes');
}
}

View File

@@ -0,0 +1,344 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
import '../../controllers/history_controller.dart' as history_controller;
class HistoryController extends GetxController {
var historyList = <Map<String, dynamic>>[].obs;
var filteredHistoryList = <Map<String, dynamic>>[].obs;
var isLoading = true.obs;
var searchKeyword = ''.obs;
var selectedSortType = 0.obs; // 0: 时间倒序, 1: 时间正序, 2: 按名称排序
var currentPage = 1.obs;
var totalPages = 1.obs;
var pageSize = 20;
var totalCount = 0.obs;
final List<String> sortTypes = ['时间倒序', '时间正序', '按名称排序'];
@override
void onInit() {
super.onInit();
loadHistory();
}
// === 加载历史记录 ===
Future<void> loadHistory() async {
isLoading.value = true;
try {
final history = await history_controller.HistoryController.getHistory();
historyList.value = history;
totalCount.value = history.length;
totalPages.value = (totalCount.value / pageSize).ceil();
filterAndSortHistory();
} catch (e) {
print('加载历史记录失败: $e');
} finally {
isLoading.value = false;
}
}
// === 搜索历史记录 ===
void searchHistory(String keyword) {
searchKeyword.value = keyword;
currentPage.value = 1;
filterAndSortHistory();
}
// === 排序历史记录 ===
void sortHistory(int sortType) {
selectedSortType.value = sortType;
currentPage.value = 1;
filterAndSortHistory();
}
// === 过滤和排序历史记录 ===
void filterAndSortHistory() {
var filtered = historyList;
// 搜索过滤
if (searchKeyword.value.isNotEmpty) {
filtered = historyList
.where((item) {
final name = item['name']?.toString().toLowerCase() ?? '';
final alias = item['alias']?.toString().toLowerCase() ?? '';
final introduce = item['introduce']?.toString().toLowerCase() ?? '';
final keyword = searchKeyword.value.toLowerCase();
return name.contains(keyword) ||
alias.contains(keyword) ||
introduce.contains(keyword);
})
.toList()
.obs;
}
// 排序
final sortedList = List<Map<String, dynamic>>.from(filtered);
switch (selectedSortType.value) {
case 0: // 时间倒序
sortedList.sort(
(a, b) => (b['timestamp'] ?? 0).compareTo(a['timestamp'] ?? 0),
);
break;
case 1: // 时间正序
sortedList.sort(
(a, b) => (a['timestamp'] ?? 0).compareTo(b['timestamp'] ?? 0),
);
break;
case 2: // 按名称排序
sortedList.sort((a, b) => (a['name'] ?? '').compareTo(b['name'] ?? ''));
break;
}
totalCount.value = sortedList.length;
totalPages.value = (totalCount.value / pageSize).ceil();
filteredHistoryList.value = sortedList;
}
// === 获取当前页的数据 ===
List<Map<String, dynamic>> getCurrentPageData() {
final startIndex = (currentPage.value - 1) * pageSize;
final endIndex = startIndex + pageSize;
if (startIndex >= filteredHistoryList.length) {
return [];
}
return filteredHistoryList.sublist(
startIndex,
endIndex > filteredHistoryList.length
? filteredHistoryList.length
: endIndex,
);
}
// === 切换到下一页 ===
void nextPage() {
if (currentPage.value < totalPages.value) {
currentPage.value++;
}
}
// === 切换到上一页 ===
void previousPage() {
if (currentPage.value > 1) {
currentPage.value--;
}
}
// === 跳转到指定页 ===
void goToPage(int page) {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page;
}
}
// === 清空历史记录 ===
Future<void> clearHistory() async {
try {
final success = await history_controller.HistoryController.clearHistory();
if (success) {
historyList.clear();
filteredHistoryList.clear();
totalCount.value = 0;
totalPages.value = 1;
currentPage.value = 1;
Get.snackbar('提示', '历史记录已清空');
} else {
Get.snackbar('提示', '清空失败');
}
} catch (e) {
print('清空历史记录失败: $e');
Get.snackbar('提示', '清空失败');
}
}
// === 删除单条记录 ===
Future<void> deleteHistoryItem(int index, Map<String, dynamic> item) async {
try {
final poetryId = item['id'] as int;
final success = await history_controller
.HistoryController.removeFromHistory(poetryId);
if (success) {
await loadHistory();
Get.snackbar('提示', '删除成功');
} else {
Get.snackbar('提示', '删除失败');
}
} catch (e) {
Get.snackbar('提示', '删除失败');
}
}
// === 导出历史记录 ===
Future<void> exportHistory() async {
try {
if (filteredHistoryList.isEmpty) {
Get.snackbar('提示', '无数据可导出');
return;
}
// 显示格式选择对话框
final selectedFormat = await Get.dialog<String>(
CupertinoAlertDialog(
title: const Text('选择导出格式'),
content: const Text('请选择要导出的文件格式'),
actions: [
CupertinoDialogAction(
child: const Text('JSON'),
onPressed: () => Get.back(result: 'json'),
),
CupertinoDialogAction(
child: const Text('TXT'),
onPressed: () => Get.back(result: 'txt'),
),
CupertinoDialogAction(
isDestructiveAction: true,
child: const Text('取消'),
onPressed: () => Get.back(result: null),
),
],
),
);
if (selectedFormat == null) {
return; // 用户取消
}
// 生成导出内容
String content;
String fileName;
if (selectedFormat == 'json') {
content = _generateJsonContent();
fileName =
'history_export_${DateTime.now().millisecondsSinceEpoch}.json';
} else {
content = _generateTxtContent();
fileName =
'history_export_${DateTime.now().millisecondsSinceEpoch}.txt';
}
// 保存到临时文件
final tempDir = await getTemporaryDirectory();
final file = File('${tempDir.path}/$fileName');
await file.writeAsString(content);
// 使用 share_plus 分享文件
final result = await Share.shareXFiles(
[XFile(file.path)],
subject: '历史记录导出',
text: '诗词历史记录导出文件',
);
if (result.status == ShareResultStatus.success) {
Get.snackbar('成功', '历史记录已导出并分享');
} else if (result.status == ShareResultStatus.dismissed) {
Get.snackbar('提示', '分享已取消');
}
// 清理临时文件
if (await file.exists()) {
await file.delete();
}
} catch (e) {
print('导出历史记录失败: $e');
Get.snackbar('错误', '导出失败: $e');
}
}
// === 生成 JSON 格式内容 ===
String _generateJsonContent() {
final exportData = {
'exportTime': DateTime.now().toIso8601String(),
'totalCount': filteredHistoryList.length,
'records': filteredHistoryList
.map(
(item) => {
'id': item['id'],
'name': item['name'],
'alias': item['alias'],
'date': item['date'],
'introduce': item['introduce'],
'timestamp': item['timestamp'],
},
)
.toList(),
};
return const JsonEncoder.withIndent(' ').convert(exportData);
}
// === 生成 TXT 格式内容 ===
String _generateTxtContent() {
final buffer = StringBuffer();
buffer.writeln('诗词历史记录导出');
buffer.writeln('导出时间: ${DateTime.now()}');
buffer.writeln('记录总数: ${filteredHistoryList.length}');
buffer.writeln('=' * 50);
buffer.writeln();
for (int i = 0; i < filteredHistoryList.length; i++) {
final item = filteredHistoryList[i];
buffer.writeln('${i + 1}${item['name'] ?? '未知诗词'}');
buffer.writeln('朝代: ${item['alias'] ?? '未知朝代'}');
buffer.writeln('日期: ${item['date'] ?? ''}');
if (item['introduce']?.toString().isNotEmpty == true) {
buffer.writeln('简介: ${item['introduce']}');
}
buffer.writeln('-' * 50);
buffer.writeln();
}
return buffer.toString();
}
// === 获取历史记录统计 ===
Future<void> showStatsDialog() async {
try {
final stats =
await history_controller.HistoryController.getHistoryStats();
if (stats.isEmpty) {
Get.snackbar('提示', '暂无统计数据');
return;
}
Get.dialog(
AlertDialog(
title: const Text('历史记录统计'),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('总数: ${stats['totalCount']}'),
Text('今日: ${stats['todayCount']}'),
Text('本周: ${stats['thisWeekCount']}'),
Text('本月: ${stats['thisMonthCount']}'),
const SizedBox(height: 16),
const Text('热门朝代:'),
if (stats['topDynasties'] != null &&
stats['topDynasties'] is Map) ...[
...(stats['topDynasties'] as Map<String, int>).entries
.map((entry) => Text('${entry.key}: ${entry.value}'))
.toList(),
],
],
),
),
actions: [
TextButton(onPressed: () => Get.back(), child: const Text('关闭')),
],
),
);
} catch (e) {
Get.snackbar('提示', '获取统计失败');
}
}
}

View File

@@ -0,0 +1,637 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/history_controller.dart';
import '../../../utils/http/poetry_api.dart';
import '../../../services/network_listener_service.dart';
import '../../../utils/audio_manager.dart';
import '../../../constants/app_constants.dart';
import '../../views/home/set/home_components.dart';
import '../../views/home/set/home-load.dart';
import '../../../views/profile/guide/tongji.dart';
class HomeController extends GetxController with NetworkListenerMixin {
var poetryData = Rxn<PoetryData>();
var keywordList = <String>[].obs;
var loading = false.obs;
var isLiked = false.obs;
var isLoadingLike = false.obs;
var errorMessage = ''.obs;
var starDisplay = ''.obs;
var historyList = <Map<String, dynamic>>[].obs;
var currentHistoryIndex = (-1).obs;
// 动态加载状态
var isLoadingNext = false.obs;
var sectionLoadingStates = {
'title': false,
'content': false,
'name': false,
'keywords': false,
'introduction': false,
}.obs;
@override
void onInit() {
super.onInit();
// 异步初始化所有服务,然后加载诗词
_initializeAndLoadPoetry();
}
Future<void> _initializeAndLoadPoetry() async {
try {
// 并行初始化所有服务
await Future.wait([
_initAudioManager(),
_initSecondaryButtonsManager(),
_initAutoRefresh(),
_initDebugInfo(),
_initOfflineDataManager(),
]);
// 所有初始化完成后,加载诗词
await loadPoetry();
} catch (e) {
// 初始化失败时,仍然尝试加载诗词
await loadPoetry();
}
}
Future<void> _initSecondaryButtonsManager() async {
await SecondaryButtonsManager().init();
}
Future<void> _initAudioManager() async {
await AudioManager().init();
}
Future<void> _initOfflineDataManager() async {
final offlineDataManager = OfflineDataManager();
await offlineDataManager.init();
}
Future<void> _initAutoRefresh() async {
final autoRefreshManager = AutoRefreshManager();
await autoRefreshManager.init();
autoRefreshManager.setOnRefresh(() {
loadNextPoetry();
});
if (autoRefreshManager.isEnabled) {
autoRefreshManager.setEnabled(true);
}
}
Future<void> _initDebugInfo() async {
final debugInfoManager = DebugInfoManager();
await debugInfoManager.init();
}
@override
void onClose() {
// 只停止定时器,不释放单例资源
AutoRefreshManager().stopTimer();
super.onClose();
}
Future<void> loadPoetry() async {
if (loading.value) return;
loading.value = true;
PoetryStateManager.triggerHapticFeedback();
try {
final offlineDataManager = OfflineDataManager();
final isOnline = await offlineDataManager.isOnline();
final hasCachedData = await offlineDataManager.hasCachedData();
if (isOnline) {
// 在线状态:从网络加载
final response = await PoetryApi.getRandomPoetry();
if (response.data != null) {
// 记录浏览统计
try {
await StatisticsManager().recordView();
await StatisticsManager().recordFirstUse();
await StatisticsManager().recordTotalView();
} catch (e) {
// 忽略错误
}
poetryData.value = response.data;
keywordList.value = PoetryDataUtils.extractKeywords(response.data);
starDisplay.value = PoetryDataUtils.getStarDisplay(response.data);
isLiked.value = false;
loading.value = false;
errorMessage.value = '';
checkIfLiked();
await saveToHistory(response.data!);
DebugInfoManager().showRefreshSuccess();
update(); // 通知UI更新
} else {
// 数据为空时,显示默认内容
poetryData.value = createDefaultPoetryData();
keywordList.value = [];
starDisplay.value = '⭐⭐⭐⭐⭐';
isLiked.value = false;
loading.value = false;
errorMessage.value = '';
DebugInfoManager().showRefreshFailed();
update(); // 通知UI更新
}
} else {
// 离线状态:从本地缓存加载
if (hasCachedData) {
final poetryData = await offlineDataManager.getNextPoetry();
if (poetryData != null) {
// 记录浏览统计
try {
await StatisticsManager().recordView();
await StatisticsManager().recordFirstUse();
await StatisticsManager().recordTotalView();
} catch (e) {
// 忽略错误
}
this.poetryData.value = poetryData;
keywordList.value = PoetryDataUtils.extractKeywords(poetryData);
starDisplay.value = PoetryDataUtils.getStarDisplay(poetryData);
isLiked.value = false;
loading.value = false;
errorMessage.value = '';
checkIfLiked();
DebugInfoManager().showRefreshSuccess();
update(); // 通知UI更新
} else {
// 缓存为空时,显示错误信息
loading.value = false;
errorMessage.value = '离线模式下无缓存数据,请先在线下载';
DebugInfoManager().showRefreshFailed();
update(); // 通知UI更新
}
} else {
// 离线且无缓存时,显示错误信息
loading.value = false;
errorMessage.value = '离线模式下无缓存数据,请先在线下载';
DebugInfoManager().showRefreshFailed();
update(); // 通知UI更新
}
}
} catch (e) {
loading.value = false;
errorMessage.value = '加载失败,请检查网络连接';
DebugInfoManager().showRefreshFailed();
update(); // 通知UI更新
}
}
// 创建默认诗词数据,确保页面始终有内容显示
PoetryData createDefaultPoetryData() {
final now = DateTime.now();
final dateStr = now.toString().substring(0, 10);
final monthStr = dateStr.substring(0, 7);
final timeStr = now.toString().substring(11, 19);
return PoetryData(
id: 1,
name: '静夜思',
alias: '李白',
keywords: '思乡,月亮,静夜',
introduce: '床前明月光,疑是地上霜。举头望明月,低头思故乡。',
drtime: '唐·李白《静夜思》',
like: 0,
url: '李白-静夜思',
tui: 1,
star: 5,
hitsTotal: 10000,
hitsMonth: 1000,
hitsDay: 100,
date: dateStr,
datem: monthStr,
time: timeStr,
createTime: timeStr,
updateTime: timeStr,
);
}
Future<void> loadPoetryById(int poetryId) async {
if (loading.value) return;
loading.value = true;
try {
final response = await PoetryApi.getPoetryById(poetryId);
if (response.data != null) {
// 记录浏览统计
try {
await StatisticsManager().recordView();
await StatisticsManager().recordFirstUse();
await StatisticsManager().recordTotalView();
} catch (e) {
// 忽略错误
}
poetryData.value = response.data;
keywordList.value = PoetryDataUtils.extractKeywords(response.data);
starDisplay.value = PoetryDataUtils.getStarDisplay(response.data);
loading.value = false;
errorMessage.value = '';
update(); // 通知UI更新
checkIfLiked();
updateCurrentHistoryIndex();
} else {
// 数据为空时,显示默认内容
poetryData.value = createDefaultPoetryData();
keywordList.value = [];
starDisplay.value = '⭐⭐⭐⭐⭐';
isLiked.value = false;
loading.value = false;
errorMessage.value = '';
update(); // 通知UI更新
}
} catch (e) {
poetryData.value = createDefaultPoetryData();
keywordList.value = [];
starDisplay.value = '⭐⭐⭐⭐⭐';
isLiked.value = false;
loading.value = false;
errorMessage.value = '';
update(); // 通知UI更新
}
}
Future<void> toggleLike() async {
if (poetryData.value == null || isLoadingLike.value) return;
// 播放点赞音效(不等待完成)
AudioManager().playLikeSound();
// 立即切换按钮状态和显示加载
isLoadingLike.value = true;
// 发送网络加载开始事件
startNetworkLoading('toggle_like');
try {
final response = await PoetryApi.toggleLike(poetryData.value!.id);
// 根据API响应消息直接判断状态
isLiked.value = response.message.contains('点赞成功');
// 更新诗词数据
if (response.data != null) {
poetryData.value = PoetryData(
id: poetryData.value!.id,
name: poetryData.value!.name,
alias: poetryData.value!.alias,
keywords: poetryData.value!.keywords,
introduce: poetryData.value!.introduce,
drtime: poetryData.value!.drtime,
like: response.data!.like,
url: poetryData.value!.url,
tui: poetryData.value!.tui,
star: poetryData.value!.star,
hitsTotal: poetryData.value!.hitsTotal,
hitsMonth: poetryData.value!.hitsMonth,
hitsDay: poetryData.value!.hitsDay,
date: poetryData.value!.date,
datem: poetryData.value!.datem,
time: poetryData.value!.time,
createTime: poetryData.value!.createTime,
updateTime: poetryData.value!.updateTime,
);
update(); // 通知UI更新
}
// 通知UI更新点赞状态
update();
// 管理点赞存储
if (isLiked.value) {
// 添加到点赞列表
await HistoryController.addToLiked(poetryData.value!.toJson());
// 记录今日点赞
await StatisticsManager().recordTodayLike();
// 记录累计点赞
await StatisticsManager().recordTotalLike();
} else {
// 从点赞列表移除
await HistoryController.removeLikedPoetry(
poetryData.value!.id.toString(),
);
}
// 发送点赞事件通知其他页面
sendLikeEvent(poetryData.value!.id.toString(), isLiked.value);
if (isLiked.value) {
DebugInfoManager().showLiked();
} else {
DebugInfoManager().showUnliked();
}
} catch (e) {
Get.snackbar(
'提示',
'操作失败,请重试',
snackPosition: SnackPosition.TOP,
backgroundColor: AppConstants.errorColor.withValues(alpha: 0.8),
colorText: Colors.white,
duration: const Duration(seconds: 2),
margin: const EdgeInsets.all(16),
borderRadius: 20,
animationDuration: const Duration(milliseconds: 300),
);
} finally {
isLoadingLike.value = false;
update(); // 通知UI更新加载状态
// 发送网络加载结束事件
endNetworkLoading('toggle_like');
}
}
Future<void> loadHistory() async {
try {
final historyJson = await HistoryController.getHistory();
historyList.value = historyJson;
// 重新计算当前诗词在历史记录中的索引
if (poetryData.value != null && historyList.isNotEmpty) {
currentHistoryIndex.value = historyList.indexWhere(
(item) => item['id'] == poetryData.value!.id,
);
} else {
currentHistoryIndex.value = -1;
}
} catch (e) {
// 加载失败
}
}
Future<void> saveToHistory(PoetryData poetryData) async {
try {
final poetryMap = {
'id': poetryData.id,
'name': poetryData.name, // 诗句名称/标题
'alias': poetryData.alias, // 诗句朝代/作者
'introduce': poetryData.introduce, // 诗句译文/解释
'drtime': poetryData.drtime, // 诗句原文/内容
};
await HistoryController.addToHistory(poetryMap);
} catch (e) {
// 保存失败
}
}
void updateCurrentHistoryIndex() {
if (poetryData.value?.id != null) {
final index = historyList.indexWhere(
(item) => item['id'] == poetryData.value!.id,
);
currentHistoryIndex.value = index >= 0 ? index : -1;
}
}
Future<void> checkIfLiked() async {
// 这里可以实现检查收藏状态的逻辑
// 暂时使用简单的模拟逻辑
}
Future<void> loadNextPoetry() async {
if (isLoadingNext.value) return;
// 播放下一条音效(不等待完成)
AudioManager().playNextSound();
isLoadingNext.value = true;
// 设置所有区域为加载状态
sectionLoadingStates.value = {
'title': true,
'content': true,
'name': true,
'keywords': true,
'introduction': true,
};
try {
final offlineDataManager = OfflineDataManager();
final isOnline = await offlineDataManager.isOnline();
final hasCachedData = await offlineDataManager.hasCachedData();
PoetryData? newPoetryData;
if (isOnline) {
// 在线状态:从网络加载
// 确保历史记录已加载
if (historyList.isEmpty) {
await loadHistory();
}
if (currentHistoryIndex.value < 0 ||
currentHistoryIndex.value >= historyList.length - 1) {
// 如果没有下一条了,加载新的诗词
final response = await PoetryApi.getRandomPoetry();
newPoetryData = response.data;
if (newPoetryData != null) {
await saveToHistory(newPoetryData);
}
} else {
// 如果有下一条,加载下一条
final nextPoetry = historyList[currentHistoryIndex.value + 1];
final response = await PoetryApi.getPoetryById(nextPoetry['id']);
newPoetryData = response.data;
}
} else {
// 离线状态:从本地缓存加载
if (hasCachedData) {
newPoetryData = await offlineDataManager.getNextPoetry();
} else {
// 离线且无缓存时,显示错误信息
Get.snackbar(
'提示',
'离线模式下无缓存数据,请先在线下载',
snackPosition: SnackPosition.TOP,
backgroundColor: AppConstants.errorColor.withValues(alpha: 0.8),
colorText: Colors.white,
duration: const Duration(seconds: 2),
margin: const EdgeInsets.all(16),
borderRadius: 20,
animationDuration: const Duration(milliseconds: 300),
);
// 重置所有加载状态
var states = <String, bool>{};
sectionLoadingStates.forEach((key, value) {
states[key] = false;
});
sectionLoadingStates.assignAll(states);
return;
}
}
if (newPoetryData != null) {
// 模拟分步加载
await simulateSectionLoading(newPoetryData);
DebugInfoManager().showNextSuccess();
}
} catch (e) {
DebugInfoManager().showNextFailed();
// 重置所有加载状态
var states = <String, bool>{};
sectionLoadingStates.forEach((key, value) {
states[key] = false;
});
sectionLoadingStates.assignAll(states);
} finally {
isLoadingNext.value = false;
}
}
// 模拟分步加载过程
Future<void> simulateSectionLoading(PoetryData newPoetryData) async {
// 记录浏览统计
try {
await StatisticsManager().recordView();
await StatisticsManager().recordFirstUse();
await StatisticsManager().recordTotalView();
} catch (e) {
// 忽略错误
}
// 1. 加载标题区域
var states = <String, bool>{};
sectionLoadingStates.forEach((key, value) {
states[key] = value;
});
states['title'] = false;
sectionLoadingStates.assignAll(states);
poetryData.value = newPoetryData;
update(); // 通知UI更新
await Future.delayed(const Duration(milliseconds: 200));
// 2. 加载诗句区域
states = <String, bool>{};
sectionLoadingStates.forEach((key, value) {
states[key] = value;
});
states['name'] = false;
sectionLoadingStates.assignAll(states);
update(); // 通知UI更新
await Future.delayed(const Duration(milliseconds: 200));
// 3. 加载原文区域
states = <String, bool>{};
sectionLoadingStates.forEach((key, value) {
states[key] = value;
});
states['content'] = false;
sectionLoadingStates.assignAll(states);
update(); // 通知UI更新
await Future.delayed(const Duration(milliseconds: 200));
// 4. 加载关键词区域
states = <String, bool>{};
sectionLoadingStates.forEach((key, value) {
states[key] = value;
});
states['keywords'] = false;
sectionLoadingStates.assignAll(states);
keywordList.value = PoetryDataUtils.extractKeywords(newPoetryData);
update(); // 通知UI更新
await Future.delayed(const Duration(milliseconds: 200));
// 5. 加载译文区域
states = <String, bool>{};
sectionLoadingStates.forEach((key, value) {
states[key] = value;
});
states['introduction'] = false;
sectionLoadingStates.assignAll(states);
starDisplay.value = PoetryDataUtils.getStarDisplay(newPoetryData);
isLiked.value = false;
errorMessage.value = '';
update(); // 通知UI更新
checkIfLiked();
updateCurrentHistoryIndex();
}
Future<void> loadPreviousPoetry() async {
try {
final offlineDataManager = OfflineDataManager();
final isOnline = await offlineDataManager.isOnline();
final hasCachedData = await offlineDataManager.hasCachedData();
if (isOnline) {
// 在线状态:从历史记录加载
// 确保历史记录已加载
if (historyList.isEmpty) {
await loadHistory();
}
if (currentHistoryIndex.value <= 0) {
// 如果当前索引无效或已经是第一条,则重新加载历史记录
await loadHistory();
// 如果历史记录为空,显示提示
if (historyList.isEmpty) {
DebugInfoManager().showPreviousFailed();
return;
}
// 如果当前索引无效,设置为最新的一条
if (currentHistoryIndex.value < 0) {
currentHistoryIndex.value = 0;
}
}
// 检查是否有上一条
if (currentHistoryIndex.value > 0) {
final previousPoetry = historyList[currentHistoryIndex.value - 1];
await loadPoetryById(previousPoetry['id']);
} else {
// 如果没有上一条了,显示提示
DebugInfoManager().showPreviousFailed();
}
} else {
// 离线状态:从本地缓存加载
if (hasCachedData) {
final poetryData = await offlineDataManager.getPreviousPoetry();
if (poetryData != null) {
this.poetryData.value = poetryData;
keywordList.value = PoetryDataUtils.extractKeywords(poetryData);
starDisplay.value = PoetryDataUtils.getStarDisplay(poetryData);
isLiked.value = false;
errorMessage.value = '';
update(); // 通知UI更新
checkIfLiked();
DebugInfoManager().showPreviousSuccess();
} else {
// 缓存为空时,显示错误信息
DebugInfoManager().showPreviousFailed();
}
} else {
// 离线且无缓存时,显示错误信息
DebugInfoManager().showPreviousFailed();
}
}
} catch (e) {
DebugInfoManager().showPreviousFailed();
}
}
Future<void> refreshPoetry() async {
if (poetryData.value?.id != null) {
// 如果有当前诗词ID则重新加载当前诗词
await loadPoetryById(poetryData.value!.id);
} else {
// 如果没有当前诗词ID则加载新的诗词
await loadPoetry();
}
}
}

View File

@@ -0,0 +1,21 @@
import 'package:get/get.dart';
/// 时间: 2025-03-21
/// 功能: 主导航控制器,管理底部导航栏的状态
/// 介绍: 负责管理当前选中的页面索引,提供页面切换的方法
class MainNavigationController extends GetxController {
// 当前选中的页面索引
final currentIndex = 0.obs;
// 切换页面
void switchPage(int index) {
currentIndex.value = index;
update();
}
// 刷新个人页面数据
void refreshProfileData() {
// 这里可以通过Get.find()获取ProfileController并调用其刷新方法
// 但为了避免循环依赖我们让ProfilePage自己处理刷新逻辑
}
}

View File

@@ -0,0 +1,329 @@
import 'dart:convert';
import 'dart:math' show Random;
import 'dart:io' as io;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:get/get.dart';
import '../../constants/app_constants.dart';
import '../../controllers/history_controller.dart';
import '../../controllers/shared_preferences_storage_controller.dart';
import '../isweb/wakelock_service.dart';
import '../../views/profile/guide/tongji.dart';
class ProfileController extends GetxController with WidgetsBindingObserver {
// 页面状态
var currentPage = 1.obs; // 默认显示第2页设置
var isCardExpanded = false.obs; // 个人卡片展开状态
var isStatsHidden = false.obs; // 统计数据隐藏状态
var isScreenWakeEnabled = false.obs; // 屏幕常亮状态
var startY = 0.0.obs;
// 历史记录相关
var poetryHistory = <Map<String, dynamic>>[].obs;
// 答题统计数据
var correctAnswers = 0.obs;
var weekQuestions = 0.obs;
// 统计数据
var todayViews = 0.obs;
var weekViews = 0.obs;
var firstUseTime = '未记录'.obs;
var useDays = 1.obs;
var dataSize = '0 B'.obs;
var noteCount = 0.obs;
var totalQuestions = 0.obs;
var todayQuestions = 0.obs;
var todayLikes = 0.obs;
// 可爱的emoji列表
final List<String> avatars = [
'👤',
'😊',
'🎉',
'🌟',
'🔥',
'💎',
'🌈',
'🦋',
'🌸',
'🐱',
'🐶',
'🐼',
'🐨',
'🐵',
'🦄',
'🐸',
'🐹',
'🐰',
'🦊',
'🐻',
];
// 模拟用户数据
var userData = {
'avatar': '👤', // 使用emoji代替网络图片
'nickname': '诗词爱好者',
'signature': '人生如诗,岁月如歌',
'level': 'Lv.12',
'vip': true,
'posts': 156,
'followers': 1280,
'following': 89,
'likes': 2560,
'favorites': 128,
'views': 3560,
}.obs;
@override
void onInit() {
super.onInit();
WidgetsBinding.instance.addObserver(this);
loadPoetryHistory();
loadPoetryStatistics();
loadStatistics();
}
@override
void onClose() {
WidgetsBinding.instance.removeObserver(this);
super.onClose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
// 应用恢复时刷新数据
refreshData();
}
}
// 更换头像
void changeAvatar() {
final random = Random();
userData['avatar'] = avatars[random.nextInt(avatars.length)];
}
// 刷新所有数据
Future<void> refreshData() async {
await loadPoetryStatistics();
await loadStatistics();
}
// 页面切换
void onPageChanged(int page) {
currentPage.value = page;
HapticFeedback.lightImpact();
}
// 切换卡片展开状态
void toggleCardExpanded() {
isCardExpanded.value = !isCardExpanded.value;
}
// 切换统计数据显示状态
void toggleStatsHidden() {
isStatsHidden.value = !isStatsHidden.value;
HapticFeedback.lightImpact();
}
// === 历史记录相关方法 ===
Future<void> loadPoetryHistory() async {
try {
final history = await HistoryController.getHistory();
poetryHistory.value = history;
} catch (e) {
print('加载历史记录失败: $e');
}
}
// === 答题统计相关方法 ===
Future<void> loadPoetryStatistics() async {
try {
// 加载总体统计
correctAnswers.value = await SharedPreferencesStorageController.getInt(
'correctAnswers',
defaultValue: 0,
);
// 加载答题记录列表来计算今日和本周答题数
List<String> records =
await SharedPreferencesStorageController.getStringList(
'poetryAnswerRecords',
defaultValue: [],
);
final now = DateTime.now();
final todayStart = DateTime(now.year, now.month, now.day);
final weekStart = todayStart.subtract(
Duration(days: todayStart.weekday - 1),
);
todayQuestions.value = 0;
weekQuestions.value = 0;
for (String recordStr in records) {
try {
final record = jsonDecode(recordStr) as Map<String, dynamic>;
final answerTime = record['answerTime'];
if (answerTime != null) {
final time = DateTime.parse(answerTime);
if (time.isAfter(todayStart)) {
todayQuestions.value++;
}
if (time.isAfter(weekStart)) {
weekQuestions.value++;
}
}
} catch (e) {
continue;
}
}
} catch (e) {
print('加载答题统计失败: $e');
}
}
// === 加载统计数据 ===
Future<void> loadStatistics() async {
try {
// 加载浏览统计
todayViews.value = await StatisticsManager().getTodayViews();
weekViews.value = await StatisticsManager().getWeekViews();
firstUseTime.value = await StatisticsManager().getFirstUseTime();
useDays.value = await StatisticsManager().getUseDays();
dataSize.value = await StatisticsManager().getDataSize();
todayLikes.value = await StatisticsManager().getTodayLikes();
todayQuestions.value = await StatisticsManager().getTodayQuestions();
// 加载笔记总数
noteCount.value = await HistoryController.getNotesCount();
// 加载累计答题数
totalQuestions.value = await SharedPreferencesStorageController.getInt(
'totalQuestions',
defaultValue: 0,
);
} catch (e) {
print('加载统计数据失败: $e');
}
}
// === 屏幕常亮相关方法 ===
Future<void> toggleScreenWake(bool enable) async {
// Web 平台不支持 wakelock_plus
if (kIsWeb) {
Get.snackbar('提示', 'Web 平台不支持屏幕常亮功能');
return;
}
try {
// 使用 io.Platform 检测平台
final String osName = io.Platform.operatingSystem;
final String osVersion = io.Platform.operatingSystemVersion;
print('Current platform: $osName, version: $osVersion');
if (enable) {
await WakelockService.instance.enable();
Get.snackbar('提示', '屏幕常亮已开启');
} else {
await WakelockService.instance.disable();
Get.snackbar('提示', '屏幕常亮已关闭');
}
// 不再更新开关状态,由 UI 层直接控制
// isScreenWakeEnabled.value = enable;
} catch (e, stackTrace) {
print('WakelockService error: $e');
print('Stack trace: $stackTrace');
// 检查错误类型,判断是否是设备不支持
String errorMessage;
if (e.toString().contains('not supported') ||
e.toString().contains('unsupported') ||
e.toString().contains('不支持')) {
errorMessage = '该设备不支持屏幕常亮功能';
} else {
errorMessage = '屏幕常亮功能异常: $e';
}
// 不再更新开关状态,由 UI 层直接控制
// isScreenWakeEnabled.value = enable;
// 显示错误提示,但不影响开关状态
Get.dialog(
AlertDialog(
title: const Text('提示'),
content: Text(errorMessage),
actions: [
TextButton(onPressed: () => Get.back(), child: const Text('确定')),
],
),
);
}
}
// === 导航方法 ===
void navigateToHistoryPage() {
Get.toNamed('/history');
}
void navigateToAppFunSettings() {
Get.toNamed('/app-fun-settings');
}
void navigateToUserPlanPage() {
Get.toNamed('/user-plan');
}
void navigateToOfflineDataPage() {
Get.toNamed('/offline-data');
}
void navigateToPermissionPage() {
Get.toNamed('/permission');
}
void navigateToAppDataPage() {
Get.toNamed('/app-data');
}
void navigateToPrivacyPage() {
Get.toNamed('/privacy');
}
void navigateToLearnUsPage() {
Get.toNamed('/learn-us');
}
void navigateToAppInfoPage() {
Get.toNamed('/app-info');
}
void navigateToAppDiyPage() {
Get.toNamed('/app-diy');
}
void navigateToVotePage() {
Get.toNamed('/vote');
}
void navigateToPoetryLevelPage() {
Get.toNamed('/poetry-level');
}
void navigateToEntirePage() {
Get.toNamed('/entire');
}
void navigateToManuscriptPage() {
Get.toNamed('/manuscript');
}
// === 显示提示 ===
void showSnackBar(String message) {
Get.snackbar('提示', message);
}
}

View File

@@ -0,0 +1,89 @@
// 时间: 2026-04-02
// 功能: Tap沉浸光感导航栏控制器
// 介绍: 管理液态玻璃导航栏的开关状态和透明度级别
// 最新变化: 添加透明度级别控制(弱、中、强)
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../config/app_config.dart';
/// 透明度级别枚举
enum TransparencyLevel {
weak, // 弱 - 当前效果
medium, // 中 - 中等透明度
strong, // 强 - 几乎透明
}
class TapLiquidGlassController extends GetxController {
SharedPreferences? _prefs;
final _isEnabled = true.obs;
final _transparencyLevel = TransparencyLevel.weak.obs;
bool get isEnabled => _isEnabled.value;
RxBool get isEnabledRx => _isEnabled;
TransparencyLevel get transparencyLevel => _transparencyLevel.value;
Rx<TransparencyLevel> get transparencyLevelRx => _transparencyLevel;
/// 获取透明度级别的索引(用于视图层)
int get transparencyLevelIndex => _transparencyLevel.value.index;
/// 获取透明度配置
/// 返回整体背景透明度
Map<String, double> get transparencyValues {
switch (_transparencyLevel.value) {
case TransparencyLevel.weak:
return {'backgroundOpacity': 0.5};
case TransparencyLevel.medium:
return {'backgroundOpacity': 0.2};
case TransparencyLevel.strong:
return {'backgroundOpacity': 0.01};
}
}
/// 获取透明度级别的中文名称
String get transparencyLevelLabel {
switch (_transparencyLevel.value) {
case TransparencyLevel.weak:
return '';
case TransparencyLevel.medium:
return '';
case TransparencyLevel.strong:
return '';
}
}
/// 通过索引设置透明度级别
Future<void> setTransparencyLevelByIndex(int index) async {
final level = TransparencyLevel
.values[index.clamp(0, TransparencyLevel.values.length - 1)];
await setTransparencyLevel(level);
}
@override
void onInit() {
super.onInit();
_loadSettings();
}
Future<void> _loadSettings() async {
_prefs = await SharedPreferences.getInstance();
_isEnabled.value = _prefs?.getBool(AppConfig.keyTapLiquidGlass) ?? true;
final levelIndex = _prefs?.getInt('tap_liquid_glass_transparency') ?? 0;
_transparencyLevel.value = TransparencyLevel
.values[levelIndex.clamp(0, TransparencyLevel.values.length - 1)];
}
Future<void> toggleEnabled(bool enabled) async {
if (_isEnabled.value == enabled) return;
_isEnabled.value = enabled;
_prefs ??= await SharedPreferences.getInstance();
await _prefs?.setBool(AppConfig.keyTapLiquidGlass, enabled);
}
Future<void> setTransparencyLevel(TransparencyLevel level) async {
if (_transparencyLevel.value == level) return;
_transparencyLevel.value = level;
_prefs ??= await SharedPreferences.getInstance();
await _prefs?.setInt('tap_liquid_glass_transparency', level.index);
}
}

View File

@@ -0,0 +1,240 @@
// 时间: 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;
}
}