深色模式、首页设置页面和功能优化
This commit is contained in:
110
lib/services/get/category_controller.dart
Normal file
110
lib/services/get/category_controller.dart
Normal 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},
|
||||
);
|
||||
}
|
||||
}
|
||||
143
lib/services/get/discover_controller.dart
Normal file
143
lib/services/get/discover_controller.dart
Normal 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('提示', '链接已复制');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
70
lib/services/get/favorites_controller.dart
Normal file
70
lib/services/get/favorites_controller.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
344
lib/services/get/history_controller.dart
Normal file
344
lib/services/get/history_controller.dart
Normal 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('提示', '获取统计失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
637
lib/services/get/home_controller.dart
Normal file
637
lib/services/get/home_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
21
lib/services/get/main_navigation_controller.dart
Normal file
21
lib/services/get/main_navigation_controller.dart
Normal 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自己处理刷新逻辑
|
||||
}
|
||||
}
|
||||
329
lib/services/get/profile_controller.dart
Normal file
329
lib/services/get/profile_controller.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
89
lib/services/get/tap_liquid_glass_controller.dart
Normal file
89
lib/services/get/tap_liquid_glass_controller.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
240
lib/services/get/theme_controller.dart
Normal file
240
lib/services/get/theme_controller.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user