关怀模式
This commit is contained in:
525
lib/services/document/api.md
Normal file
525
lib/services/document/api.md
Normal file
@@ -0,0 +1,525 @@
|
||||
# 诗词答题 API 文档
|
||||
|
||||
## 基础信息
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 基础URL | `/api.php` |
|
||||
| 返回格式 | JSON |
|
||||
| 编码 | UTF-8 |
|
||||
| 请求方式 | **GET / POST 都支持** |
|
||||
|
||||
### 请求方式说明
|
||||
|
||||
| 接口 | 推荐方式 | 原因 |
|
||||
|------|----------|------|
|
||||
| 获取题目 (question) | **GET** | 读取操作,简单可缓存 |
|
||||
| 下一题 (next) | **GET** | 读取操作 |
|
||||
| 获取新题 (fetch) | **GET** | 读取操作 |
|
||||
| 提交答案 (answer) | **POST** | 提交操作,更规范 |
|
||||
| 获取提示 (hint) | **GET** | 读取操作 |
|
||||
| 题目列表 (list) | **GET** | 读取操作 |
|
||||
| 刷新缓存 (refresh) | **GET** | 管理操作 |
|
||||
| 状态统计 (stats) | **GET** | 读取操作 |
|
||||
|
||||
## 通用返回格式
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "",
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| code | int | 状态码,0=成功,其他=错误 |
|
||||
| msg | string | 提示信息 |
|
||||
| data | object | 返回数据 |
|
||||
|
||||
---
|
||||
|
||||
## 接口列表
|
||||
|
||||
### 1. 获取题目
|
||||
|
||||
**请求**
|
||||
```
|
||||
GET /api.php?action=question&id=0
|
||||
```
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| action | string | 否 | 默认为 question |
|
||||
| id | int | 否 | 题目ID,默认0 |
|
||||
|
||||
**返回**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"id": 0,
|
||||
"total": 10,
|
||||
"question": "欲把西湖比西子,\"________\"",
|
||||
"author": "苏轼",
|
||||
"type": "江南",
|
||||
"grade": "小学",
|
||||
"dynasty": "宋朝",
|
||||
"options": [
|
||||
{"index": 1, "content": "山色空蒙雨亦奇"},
|
||||
{"index": 2, "content": "淡妆浓抹总相宜"},
|
||||
{"index": 3, "content": "门前流水尚能西"},
|
||||
{"index": 4, "content": "拄杖无时夜叩门"}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 获取下一题(自动进度)
|
||||
|
||||
**请求**
|
||||
```
|
||||
GET /api.php?action=next
|
||||
```
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| action | string | 是 | 固定为 next |
|
||||
|
||||
**说明**
|
||||
- **无需传 id 参数**,系统自动记住当前进度(使用 Session)
|
||||
- 每次刷新自动跳到下一题
|
||||
- 如果已经是最后一题,自动回到第 0 题(循环)
|
||||
|
||||
**使用方式**
|
||||
| 刷新次数 | 返回的 id |
|
||||
|----------|-----------|
|
||||
| 第 1 次 | 1 |
|
||||
| 第 2 次 | 2 |
|
||||
| 第 3 次 | 3 |
|
||||
| ... | ... |
|
||||
| 第 62 次 | 0 (循环) |
|
||||
|
||||
**返回**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"total": 62,
|
||||
"question": "人生得意须尽欢,\"__________\"",
|
||||
"author": "李白",
|
||||
"type": "豪放",
|
||||
"grade": "高中",
|
||||
"dynasty": "唐朝",
|
||||
"options": [...],
|
||||
"prev_id": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 刷新获取新题(推荐)
|
||||
|
||||
**请求**
|
||||
```
|
||||
GET /api.php?action=fetch
|
||||
```
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| action | string | 是 | 固定为 fetch |
|
||||
|
||||
**说明**
|
||||
- **每次刷新都从百度 API 获取新题目**
|
||||
- 新题目自动写入本地缓存(去重)
|
||||
- 返回本次获取的随机一题
|
||||
- API 失败时自动降级使用本地缓存
|
||||
|
||||
**返回**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"id": null,
|
||||
"total": 65,
|
||||
"question": "人生得意须尽欢,\"__________\"",
|
||||
"author": "李白",
|
||||
"type": "豪放",
|
||||
"grade": "高中",
|
||||
"dynasty": "唐朝",
|
||||
"options": [...],
|
||||
"from_cache": false,
|
||||
"new_questions": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**额外返回字段**
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| from_cache | bool | 是否来自本地缓存 |
|
||||
| new_questions | int | 本次新增题目数量 |
|
||||
|
||||
---
|
||||
|
||||
### 4. 提交答案
|
||||
|
||||
**请求方式 1:GET(简单)**
|
||||
```
|
||||
GET /api.php?action=answer&id=0&answer=2
|
||||
```
|
||||
|
||||
**请求方式 2:POST(推荐)**
|
||||
```
|
||||
POST /api.php
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
action=answer&id=0&answer=2
|
||||
```
|
||||
|
||||
或使用 JSON:
|
||||
```
|
||||
POST /api.php
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"action": "answer",
|
||||
"id": 0,
|
||||
"answer": "2"
|
||||
}
|
||||
```
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| action | string | 是 | 固定为 answer |
|
||||
| id | int | 是 | 题目ID |
|
||||
| answer | string | 是 | 答案序号(1-4) |
|
||||
|
||||
**返回**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"id": 0,
|
||||
"correct": true,
|
||||
"your_answer": "2",
|
||||
"correct_answer": "2",
|
||||
"next_id": 1,
|
||||
"has_next": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 获取提示
|
||||
|
||||
**请求**
|
||||
```
|
||||
GET /api.php?action=hint&id=0
|
||||
```
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| action | string | 是 | 固定为 hint |
|
||||
| id | int | 是 | 题目ID |
|
||||
|
||||
**返回**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"id": 0,
|
||||
"hint": "这是首描写江南的诗,你在小学学过它。",
|
||||
"author": "苏轼",
|
||||
"dynasty": "宋朝"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 获取题目列表
|
||||
|
||||
**请求**
|
||||
```
|
||||
GET /api.php?action=list
|
||||
```
|
||||
|
||||
**返回**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"total": 10,
|
||||
"list": [
|
||||
{
|
||||
"id": 0,
|
||||
"question": "欲把西湖比西子,\"________\"...",
|
||||
"author": "苏轼",
|
||||
"dynasty": "宋朝"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. 刷新缓存
|
||||
|
||||
**请求**
|
||||
```
|
||||
GET /api.php?action=refresh
|
||||
```
|
||||
|
||||
**说明**
|
||||
- 强制从百度 API 获取最新题目
|
||||
- 合并到本地缓存(去重)
|
||||
|
||||
**返回**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"refreshed": true,
|
||||
"total": 15
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 缓存机制
|
||||
|
||||
| 配置 | 值 | 说明 |
|
||||
|------|------|------|
|
||||
| 缓存文件 | `data/questions.json` | 本地JSON文件 |
|
||||
| 过期时间 | **永不过期** | 永久保存 |
|
||||
| 去重方式 | 按 `question_content` | 相同题目不重复存储 |
|
||||
| 降级策略 | API失败自动使用本地缓存 | 原始URL失效也能使用 |
|
||||
| API 限频 | 5秒内只请求一次 | 防止并发请求百度 |
|
||||
|
||||
### 缓存文件结构
|
||||
|
||||
```json
|
||||
{
|
||||
"updated": "2024-01-01 12:00:00",
|
||||
"count": 10,
|
||||
"questions": [...]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 已实现优化
|
||||
|
||||
| 优化项 | 说明 |
|
||||
|--------|------|
|
||||
| 静态变量缓存 | 同一请求内多次读取只加载一次文件 |
|
||||
| API 请求锁 | 5秒内多人请求只发一次 API |
|
||||
| 原子写入 | 先写临时文件再 rename,防止数据损坏 |
|
||||
| 去重优化 | 只在写入时去重,读取时不处理 |
|
||||
| 超时控制 | API 超时 5 秒,连接超时 3 秒 |
|
||||
|
||||
### 性能预估
|
||||
|
||||
| 场景 | 响应时间 |
|
||||
|------|----------|
|
||||
| 读取本地缓存 | ~2-5ms |
|
||||
| API 请求成功 | ~200-500ms |
|
||||
| API 限频降级 | ~1ms(直接读缓存) |
|
||||
| 100人并发 | 无压力(读缓存为主) |
|
||||
|
||||
### 查看状态
|
||||
|
||||
```
|
||||
GET /api.php?action=stats
|
||||
```
|
||||
|
||||
返回:
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"total_questions": 149,
|
||||
"cache_file_size": "40.5 KB",
|
||||
"last_updated": "2026-03-29 05:04:04",
|
||||
"memory_usage": "256 KB"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 错误码
|
||||
|
||||
| code | 说明 |
|
||||
|------|------|
|
||||
| 0 | 成功 |
|
||||
| 400 | 参数错误 |
|
||||
| 404 | 题目不存在 |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### JavaScript
|
||||
```javascript
|
||||
// 获取第一题
|
||||
fetch('/api.php?action=question&id=0')
|
||||
.then(r => r.json())
|
||||
.then(d => console.log(d.data.question));
|
||||
|
||||
// 获取下一题(自动进度,无需传 id)
|
||||
fetch('/api.php?action=next')
|
||||
.then(r => r.json())
|
||||
.then(d => console.log(d.data.id, d.data.question));
|
||||
|
||||
// 提交答案
|
||||
fetch('/api.php?action=answer&id=0&answer=2')
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
if(d.data.correct) {
|
||||
console.log('回答正确!');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Flutter/Dart
|
||||
```dart
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
final dio = Dio();
|
||||
|
||||
// 获取题目
|
||||
Future<Map> getQuestion(int id) async {
|
||||
final res = await dio.get('/api.php', queryParameters: {
|
||||
'action': 'question',
|
||||
'id': id,
|
||||
});
|
||||
return res.data['data'];
|
||||
}
|
||||
|
||||
// 获取下一题(自动进度,无需传 id)
|
||||
Future<Map> getNextQuestion() async {
|
||||
final res = await dio.get('/api.php', queryParameters: {
|
||||
'action': 'next',
|
||||
});
|
||||
return res.data['data'];
|
||||
}
|
||||
|
||||
// 提交答案(POST 方式)
|
||||
Future<bool> checkAnswer(int id, String answer) async {
|
||||
final res = await dio.post('/api.php', data: {
|
||||
'action': 'answer',
|
||||
'id': id,
|
||||
'answer': answer,
|
||||
});
|
||||
return res.data['data']['correct'];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## App 集成建议
|
||||
|
||||
### 方案 1:App 自己管理进度(推荐)
|
||||
|
||||
使用 `?action=question` 接口,App 完全控制进度:
|
||||
|
||||
```dart
|
||||
class QuizApi {
|
||||
int _currentId = 0;
|
||||
int _total = 0;
|
||||
|
||||
Future<Map> getQuestion() async {
|
||||
final res = await dio.get('/api.php', queryParameters: {
|
||||
'action': 'question',
|
||||
'id': _currentId,
|
||||
});
|
||||
final data = res.data['data'];
|
||||
_total = data['total'] ?? 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
Future<Map> submitAnswer(String answer) async {
|
||||
final res = await dio.post('/api.php', data: {
|
||||
'action': 'answer',
|
||||
'id': _currentId,
|
||||
'answer': answer,
|
||||
});
|
||||
return res.data['data'];
|
||||
}
|
||||
|
||||
void nextQuestion() {
|
||||
_currentId++;
|
||||
if (_currentId >= _total) {
|
||||
_currentId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int get currentId => _currentId;
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 不依赖 Session,App 完全控制进度
|
||||
- 可以随时跳转任意题目
|
||||
- 适合多端同步
|
||||
|
||||
---
|
||||
|
||||
### 方案 2:使用自动进度
|
||||
|
||||
使用 `?action=next` 接口,API 自动管理:
|
||||
|
||||
```dart
|
||||
Future<Map> getNextQuestion() async {
|
||||
final res = await dio.get('/api.php', queryParameters: {
|
||||
'action': 'next',
|
||||
});
|
||||
return res.data['data'];
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 简单,无需管理 ID
|
||||
- 自动循环
|
||||
|
||||
**缺点**:
|
||||
- 依赖 Session,需要保持 cookie
|
||||
- 无法自由跳转题目
|
||||
|
||||
---
|
||||
|
||||
### 方案 3:不断获取新题
|
||||
|
||||
使用 `?action=fetch` 接口,每次都从百度获取新题:
|
||||
|
||||
```dart
|
||||
Future<Map> getNewQuestion() async {
|
||||
final res = await dio.get('/api.php', queryParameters: {
|
||||
'action': 'fetch',
|
||||
});
|
||||
return res.data['data'];
|
||||
}
|
||||
```
|
||||
|
||||
**适用场景**:
|
||||
- 想要不断扩展题库
|
||||
- 用户每次刷新都可能看到新题
|
||||
87
lib/services/get/care_controller.dart
Normal file
87
lib/services/get/care_controller.dart
Normal file
@@ -0,0 +1,87 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class CareController extends GetxController {
|
||||
static const String _careModeKey = 'care_mode_enabled';
|
||||
static const String _userTypeKey = 'care_user_type';
|
||||
static const String _pinyinEnabledKey = 'care_pinyin_enabled';
|
||||
static const String _selectedOptionsKey = 'care_selected_options';
|
||||
static const String _careNavigationIndexKey = 'care_navigation_index';
|
||||
|
||||
final RxBool _isCareModeEnabled = false.obs;
|
||||
final RxString _userType = '儿童'.obs;
|
||||
final RxBool _pinyinEnabled = false.obs;
|
||||
final RxSet<String> _selectedOptions = {'诗词', '出处'}.obs;
|
||||
final RxBool isCareButtonVisible = false.obs;
|
||||
final RxInt _careNavigationIndex = 0.obs;
|
||||
|
||||
bool get isCareModeEnabled => _isCareModeEnabled.value;
|
||||
String get userType => _userType.value;
|
||||
bool get pinyinEnabled => _pinyinEnabled.value;
|
||||
Set<String> get selectedOptions => _selectedOptions.toSet();
|
||||
int get careNavigationIndex => _careNavigationIndex.value;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
_loadCareSettings();
|
||||
}
|
||||
|
||||
Future<void> _loadCareSettings() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
_isCareModeEnabled.value = prefs.getBool(_careModeKey) ?? false;
|
||||
_userType.value = prefs.getString(_userTypeKey) ?? '儿童';
|
||||
_pinyinEnabled.value = prefs.getBool(_pinyinEnabledKey) ?? false;
|
||||
_careNavigationIndex.value = prefs.getInt(_careNavigationIndexKey) ?? 0;
|
||||
|
||||
final savedOptions = prefs.getStringList(_selectedOptionsKey);
|
||||
if (savedOptions != null && savedOptions.isNotEmpty) {
|
||||
_selectedOptions.clear();
|
||||
_selectedOptions.addAll(savedOptions);
|
||||
if (!_selectedOptions.contains('诗词')) {
|
||||
_selectedOptions.add('诗词');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> toggleCareMode() async {
|
||||
_isCareModeEnabled.value = !_isCareModeEnabled.value;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool(_careModeKey, _isCareModeEnabled.value);
|
||||
}
|
||||
|
||||
Future<void> setUserType(String type) async {
|
||||
_userType.value = type;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_userTypeKey, type);
|
||||
}
|
||||
|
||||
Future<void> setPinyinEnabled(bool enabled) async {
|
||||
_pinyinEnabled.value = enabled;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool(_pinyinEnabledKey, enabled);
|
||||
}
|
||||
|
||||
Future<void> toggleOption(String option) async {
|
||||
if (option == '诗词') return; // 诗词选项不可取消
|
||||
|
||||
if (_selectedOptions.contains(option)) {
|
||||
_selectedOptions.remove(option);
|
||||
} else {
|
||||
_selectedOptions.add(option);
|
||||
}
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setStringList(_selectedOptionsKey, _selectedOptions.toList());
|
||||
}
|
||||
|
||||
void toggleCareButtonVisibility() {
|
||||
isCareButtonVisible.value = !isCareButtonVisible.value;
|
||||
}
|
||||
|
||||
Future<void> switchCareNavigation(int index) async {
|
||||
_careNavigationIndex.value = index;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setInt(_careNavigationIndexKey, index);
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,13 @@ class FavoritesController extends GetxController {
|
||||
}
|
||||
|
||||
void showFilterOptions(BuildContext context) {
|
||||
// 先获取当前值,避免在弹窗中使用 Obx
|
||||
final currentSortByTime = sortByTime.value;
|
||||
final currentLikesFirst = likesFirst.value;
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => Container(
|
||||
builder: (BuildContext context) => Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -43,54 +47,51 @@ class FavoritesController extends GetxController {
|
||||
ListTile(
|
||||
leading: const Icon(Icons.date_range),
|
||||
title: const Text('按时间排序'),
|
||||
trailing: Obx(
|
||||
() => Icon(
|
||||
sortByTime.value
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
trailing: Icon(
|
||||
currentSortByTime
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
sortByTime.value = true;
|
||||
Get.snackbar('提示', '已按时间排序');
|
||||
// 触发排序更新
|
||||
update();
|
||||
// 使用 Future.delayed 确保弹窗完全关闭后再显示 snackbar
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
Get.snackbar('提示', '已按时间排序');
|
||||
});
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.title),
|
||||
title: const Text('按分类排序'),
|
||||
trailing: Obx(
|
||||
() => Icon(
|
||||
!sortByTime.value
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
trailing: Icon(
|
||||
!currentSortByTime
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
sortByTime.value = false;
|
||||
Get.snackbar('提示', '已按分类排序');
|
||||
// 触发排序更新
|
||||
update();
|
||||
// 使用 Future.delayed 确保弹窗完全关闭后再显示 snackbar
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
Get.snackbar('提示', '已按分类排序');
|
||||
});
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.swap_vert),
|
||||
title: Text(likesFirst.value ? '点赞在前' : '笔记在前'),
|
||||
trailing: Obx(
|
||||
() => Icon(Icons.swap_vert, color: AppConstants.primaryColor),
|
||||
),
|
||||
title: Text(currentLikesFirst ? '点赞在前' : '笔记在前'),
|
||||
trailing: Icon(Icons.swap_vert, color: AppConstants.primaryColor),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
likesFirst.value = !likesFirst.value;
|
||||
Get.snackbar('提示', likesFirst.value ? '点赞在前' : '笔记在前');
|
||||
// 触发顺序更新
|
||||
update();
|
||||
// 使用 Future.delayed 确保弹窗完全关闭后再显示 snackbar
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
Get.snackbar('提示', likesFirst.value ? '点赞在前' : '笔记在前');
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../models/colors/theme_colors.dart';
|
||||
import '../../models/night-mode/theme_model.dart';
|
||||
|
||||
/// 主题控制器
|
||||
@@ -40,6 +41,14 @@ class ThemeController extends GetxController {
|
||||
bool get enableAnimation => _enableAnimation.value;
|
||||
bool get enableBlurEffect => _enableBlurEffect.value;
|
||||
|
||||
// 获取当前主题颜色
|
||||
Color get currentThemeColor =>
|
||||
ThemeColors.getThemeColor(_themeColorIndex.value);
|
||||
|
||||
// 获取当前强调色
|
||||
Color get currentAccentColor =>
|
||||
ThemeColors.getAccentColor(_accentColorIndex.value);
|
||||
|
||||
/// 获取当前 Flutter ThemeMode
|
||||
ThemeMode get currentThemeMode {
|
||||
if (_isDarkMode.value) {
|
||||
@@ -71,9 +80,11 @@ class ThemeController extends GetxController {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
|
||||
_isDarkMode.value = _prefs?.getBool(_darkModeKey) ?? false;
|
||||
_themeMode.value = AppThemeMode.values[
|
||||
(_prefs?.getInt(_themeModeKey) ?? 0).clamp(0, AppThemeMode.values.length - 1)
|
||||
];
|
||||
_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;
|
||||
@@ -102,6 +113,14 @@ class ThemeController extends GetxController {
|
||||
Get.changeThemeMode(currentThemeMode);
|
||||
}
|
||||
|
||||
/// 应用主题颜色到 GetX
|
||||
void _applyThemeColor() {
|
||||
// 这里可以根据需要创建新的主题并应用
|
||||
// 由于我们使用的是 GetX 的主题系统,
|
||||
// 我们可以通过更新全局状态来让组件响应主题颜色变化
|
||||
update();
|
||||
}
|
||||
|
||||
/// 切换深色模式
|
||||
/// [enabled] true 开启深色模式, false 关闭深色模式
|
||||
Future<void> toggleDarkMode(bool enabled) async {
|
||||
@@ -144,6 +163,17 @@ class ThemeController extends GetxController {
|
||||
|
||||
_themeColorIndex.value = index;
|
||||
await _saveThemeSettings();
|
||||
_applyThemeColor();
|
||||
|
||||
// 显示提示
|
||||
Get.snackbar(
|
||||
'主题颜色',
|
||||
'已更换主题颜色',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
duration: const Duration(seconds: 2),
|
||||
margin: const EdgeInsets.all(16),
|
||||
borderRadius: 12,
|
||||
);
|
||||
}
|
||||
|
||||
/// 设置强调色索引
|
||||
@@ -152,6 +182,17 @@ class ThemeController extends GetxController {
|
||||
|
||||
_accentColorIndex.value = index;
|
||||
await _saveThemeSettings();
|
||||
_applyThemeColor();
|
||||
|
||||
// 显示提示
|
||||
Get.snackbar(
|
||||
'强调颜色',
|
||||
'已更换强调颜色',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
duration: const Duration(seconds: 2),
|
||||
margin: const EdgeInsets.all(16),
|
||||
borderRadius: 12,
|
||||
);
|
||||
}
|
||||
|
||||
/// 设置字体大小索引
|
||||
|
||||
Reference in New Issue
Block a user