声音功能

This commit is contained in:
Developer
2026-03-30 21:30:11 +08:00
parent ecffddbc6f
commit 4cd7629f61
15 changed files with 703 additions and 126 deletions

View File

@@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.
--- ---
## [1.3.7] - 2026-03-30
### 新增
- 🎵 **点击音效功能**
- 新增音频管理类 `lib/utils/audio_manager.dart`使用audioplayers库管理音效播放
- 在首页点击诗词卡片时播放音效 `assets/audios/deep.mp3`
- 在点击"下一条"按钮时播放音效
- 在点击"点赞"按钮时播放音效
- 修改 `lib/views/home/home_page.dart`,初始化音频管理器并在点赞、下一条事件中添加音效
- 修改 `lib/views/home/home_part.dart`,在诗词卡片点击事件中添加音效
- 支持静音控制可通过AudioManager设置静音状态
### 修复
- 📁 **音频文件声明**
-`pubspec.yaml` 中添加 `assets/audios/deep.mp3` 音频文件的声明,确保应用能正确加载音频资源
---
## [1.3.6] - 2026-03-30 ## [1.3.6] - 2026-03-30
### 新增 ### 新增

BIN
assets/audios/deep.mp3 Normal file

Binary file not shown.

View File

@@ -0,0 +1,97 @@
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/foundation.dart';
/// 时间: 2026-03-30
/// 功能: 音频管理类
/// 介绍: 管理应用中的音效播放,包括点击音效、点赞音效等
/// 最新变化: 新增音频播放功能
class AudioManager {
static AudioManager? _instance;
late AudioPlayer _audioPlayer;
bool _isInitialized = false;
bool _isMuted = false;
AudioManager._internal() {
_audioPlayer = AudioPlayer();
}
factory AudioManager() {
_instance ??= AudioManager._internal();
return _instance!;
}
Future<void> init() async {
if (_isInitialized) return;
try {
// 设置音频播放器模式
await _audioPlayer.setPlayerMode(PlayerMode.lowLatency);
_isInitialized = true;
_debugLog('音频管理器初始化成功');
} catch (e) {
_debugLog('音频管理器初始化失败: $e');
}
}
/// 播放点击音效
Future<void> playClickSound() async {
if (_isMuted) return;
await _playSound('audios/deep.mp3');
}
/// 播放点赞音效
Future<void> playLikeSound() async {
if (_isMuted) return;
await _playSound('audios/deep.mp3');
}
/// 播放下一条音效
Future<void> playNextSound() async {
if (_isMuted) return;
await _playSound('audios/deep.mp3');
}
/// 通用播放方法
Future<void> _playSound(String assetPath) async {
if (!_isInitialized) {
await init();
}
try {
// 停止当前播放的音频
await _audioPlayer.stop();
// 播放新音频
await _audioPlayer.play(AssetSource(assetPath));
_debugLog('播放音效: $assetPath');
} catch (e) {
_debugLog('播放音效失败: $e');
}
}
/// 设置静音状态
void setMuted(bool muted) {
_isMuted = muted;
_debugLog('静音状态: $_isMuted');
}
/// 获取静音状态
bool get isMuted => _isMuted;
/// 释放资源
Future<void> dispose() async {
try {
await _audioPlayer.dispose();
_isInitialized = false;
_debugLog('音频管理器已释放');
} catch (e) {
_debugLog('释放音频管理器失败: $e');
}
}
void _debugLog(String message) {
if (kDebugMode) {
print('[AudioManager] $message');
}
}
}

View File

@@ -10,6 +10,7 @@ import '../../constants/app_constants.dart';
import '../../../controllers/history_controller.dart'; import '../../../controllers/history_controller.dart';
import '../../../utils/http/poetry_api.dart'; import '../../../utils/http/poetry_api.dart';
import '../../../services/network_listener_service.dart'; import '../../../services/network_listener_service.dart';
import '../../../utils/audio_manager.dart';
import 'home_part.dart'; import 'home_part.dart';
import 'home_components.dart'; import 'home_components.dart';
import 'home-load.dart'; import 'home-load.dart';
@@ -57,12 +58,17 @@ class _HomePageState extends State<HomePage>
_initAutoRefresh(); _initAutoRefresh();
_initDebugInfo(); _initDebugInfo();
_initOfflineDataManager(); _initOfflineDataManager();
_initAudioManager();
// 延迟加载诗词,确保页面先显示 // 延迟加载诗词,确保页面先显示
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
_loadPoetry(); _loadPoetry();
}); });
} }
Future<void> _initAudioManager() async {
await AudioManager().init();
}
Future<void> _initOfflineDataManager() async { Future<void> _initOfflineDataManager() async {
final offlineDataManager = OfflineDataManager(); final offlineDataManager = OfflineDataManager();
await offlineDataManager.init(); await offlineDataManager.init();
@@ -287,6 +293,9 @@ class _HomePageState extends State<HomePage>
Future<void> _toggleLike() async { Future<void> _toggleLike() async {
if (_poetryData == null || _isLoadingLike) return; if (_poetryData == null || _isLoadingLike) return;
// 播放点赞音效
await AudioManager().playLikeSound();
// 立即切换按钮状态和显示加载 // 立即切换按钮状态和显示加载
setState(() { setState(() {
_isLoadingLike = true; _isLoadingLike = true;
@@ -425,6 +434,9 @@ class _HomePageState extends State<HomePage>
void _loadNextPoetry() async { void _loadNextPoetry() async {
if (_isLoadingNext) return; if (_isLoadingNext) return;
// 播放下一条音效
await AudioManager().playNextSound();
setState(() { setState(() {
_isLoadingNext = true; _isLoadingNext = true;
// 设置所有区域为加载状态 // 设置所有区域为加载状态

View File

@@ -5,8 +5,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../constants/app_constants.dart'; import '../../../constants/app_constants.dart';
import '../../../utils/http/poetry_api.dart'; import '../../../utils/http/poetry_api.dart';
import '../../../utils/flutter_compatibility_fix.dart';
import '../../../utils/audio_manager.dart';
import 'home_components.dart'; import 'home_components.dart';
/// 诗词卡片组件 - 优化版本,防止拉伸和处理文本溢出 /// 诗词卡片组件 - 优化版本,防止拉伸和处理文本溢出
@@ -31,6 +34,13 @@ class PoetryCard extends StatefulWidget {
class _PoetryCardState extends State<PoetryCard> { class _PoetryCardState extends State<PoetryCard> {
bool _showCopyTip = true; bool _showCopyTip = true;
bool _showRecommendation = false; bool _showRecommendation = false;
bool _globalTipsEnabled = true; // 添加全局Tips开关状态
@override
void initState() {
super.initState();
_loadGlobalTipsSettings(); // 加载全局Tips设置
}
String _getTimeOfDayGreeting() { String _getTimeOfDayGreeting() {
final hour = DateTime.now().hour; final hour = DateTime.now().hour;
@@ -74,10 +84,34 @@ class _PoetryCardState extends State<PoetryCard> {
} }
} }
// 加载全局Tips设置
Future<void> _loadGlobalTipsSettings() async {
try {
final prefs = await SharedPreferences.getInstance();
if (mounted) {
setState(() {
_globalTipsEnabled = prefs.getBool('global_tips_enabled') ?? true;
});
}
} catch (e) {
// 如果加载失败,默认开启
if (mounted) {
setState(() {
_globalTipsEnabled = true;
});
}
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: widget.onTap, onTap: () async {
// 播放点击音效
await AudioManager().playClickSound();
// 调用原始的onTap回调
widget.onTap?.call();
},
child: Stack( child: Stack(
children: [ children: [
Container( Container(
@@ -87,7 +121,7 @@ class _PoetryCardState extends State<PoetryCard> {
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withValues(alpha: 0.1), color: Colors.black.withAlpha(10),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@@ -143,7 +177,7 @@ class _PoetryCardState extends State<PoetryCard> {
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: [ colors: [
AppConstants.primaryColor.withValues(alpha: 0.8), AppConstants.primaryColor.withAlpha(204),
AppConstants.primaryColor, AppConstants.primaryColor,
], ],
begin: Alignment.topLeft, begin: Alignment.topLeft,
@@ -197,7 +231,7 @@ class _PoetryCardState extends State<PoetryCard> {
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: AppConstants.primaryColor.withValues(alpha: 0.3), color: AppConstants.primaryColor.withAlpha(76),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@@ -208,8 +242,8 @@ class _PoetryCardState extends State<PoetryCard> {
children: [ children: [
const Icon(Icons.info_outline, color: Colors.white, size: 16), const Icon(Icons.info_outline, color: Colors.white, size: 16),
const SizedBox(width: 6), const SizedBox(width: 6),
const Text( Text(
'点击任意区域加载下一条,长按复制,下拉刷新', _globalTipsEnabled ? '点击任意区域加载下一条,长按复制,下拉刷新' : '',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 12, fontSize: 12,
@@ -293,20 +327,20 @@ class _PoetryCardState extends State<PoetryCard> {
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: [ colors: [
AppConstants.primaryColor.withValues(alpha: 0.1), AppConstants.primaryColor.withAlpha(26),
AppConstants.primaryColor.withValues(alpha: 0.05), AppConstants.primaryColor.withAlpha(13),
], ],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
), ),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all( border: Border.all(
color: AppConstants.primaryColor.withValues(alpha: 0.2), color: AppConstants.primaryColor.withAlpha(51),
width: 1, width: 1,
), ),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: AppConstants.primaryColor.withValues(alpha: 0.1), color: AppConstants.primaryColor.withAlpha(26),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@@ -437,7 +471,7 @@ class _PoetryCardState extends State<PoetryCard> {
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppConstants.secondaryColor color: AppConstants.secondaryColor
.withValues(alpha: 0.1), .withAlpha(26),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Text( child: Text(
@@ -468,8 +502,8 @@ class _PoetryCardState extends State<PoetryCard> {
vertical: 4, vertical: 4,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppConstants.primaryColor.withValues( color: AppConstants.primaryColor.withAlpha(
alpha: 0.1, 26,
), ),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
@@ -604,11 +638,9 @@ class _PoetryCardState extends State<PoetryCard> {
constraints: const BoxConstraints(maxHeight: 150), constraints: const BoxConstraints(maxHeight: 150),
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppConstants.infoColor.withValues(alpha: 0.05), color: AppConstants.infoColor.withAlpha(13),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all( border: Border.all(color: AppConstants.infoColor.withAlpha(51)),
color: AppConstants.infoColor.withValues(alpha: 0.2),
),
), ),
child: isLoading child: isLoading
? Center( ? Center(
@@ -671,7 +703,7 @@ class FloatingPreviousButton extends StatelessWidget {
shape: BoxShape.circle, shape: BoxShape.circle,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: AppConstants.secondaryColor.withValues(alpha: 0.3), color: AppConstants.secondaryColor.withAlpha(76),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -711,7 +743,7 @@ class FloatingNextButton extends StatelessWidget {
shape: BoxShape.circle, shape: BoxShape.circle,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: AppConstants.primaryColor.withValues(alpha: 0.3), color: AppConstants.primaryColor.withAlpha(76),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -759,7 +791,7 @@ class FloatingLikeButton extends StatelessWidget {
BoxShadow( BoxShadow(
color: color:
(isLiked ? AppConstants.errorColor : AppConstants.primaryColor) (isLiked ? AppConstants.errorColor : AppConstants.primaryColor)
.withValues(alpha: 0.3), .withAlpha(76),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -813,7 +845,7 @@ class StatsCard extends StatelessWidget {
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withValues(alpha: 0.05), color: Colors.black.withAlpha(5),
blurRadius: 5, blurRadius: 5,
offset: const Offset(0, 1), offset: const Offset(0, 1),
), ),

View File

@@ -28,6 +28,7 @@ class _BugListPageState extends State<BugListPage> {
'resolveTime': '2026-04-15', 'resolveTime': '2026-04-15',
'reportTime': '2026-03-25', 'reportTime': '2026-03-25',
'affectedUsers': '部分用户', 'affectedUsers': '部分用户',
'expanded': false, // 添加展开状态
}, },
{ {
'id': 2, 'id': 2,
@@ -39,6 +40,7 @@ class _BugListPageState extends State<BugListPage> {
'resolveTime': '2026-04-10', 'resolveTime': '2026-04-10',
'reportTime': '2026-03-20', 'reportTime': '2026-03-20',
'affectedUsers': '大量用户', 'affectedUsers': '大量用户',
'expanded': false, // 添加展开状态
}, },
{ {
'id': 3, 'id': 3,
@@ -50,6 +52,7 @@ class _BugListPageState extends State<BugListPage> {
'resolveTime': '2026-03-28', 'resolveTime': '2026-03-28',
'reportTime': '2026-03-15', 'reportTime': '2026-03-15',
'affectedUsers': '少数用户', 'affectedUsers': '少数用户',
'expanded': false, // 添加展开状态
}, },
{ {
'id': 4, 'id': 4,
@@ -61,6 +64,7 @@ class _BugListPageState extends State<BugListPage> {
'resolveTime': '2026-04-20', 'resolveTime': '2026-04-20',
'reportTime': '2026-03-22', 'reportTime': '2026-03-22',
'affectedUsers': '部分用户', 'affectedUsers': '部分用户',
'expanded': false, // 添加展开状态
}, },
{ {
'id': 5, 'id': 5,
@@ -72,12 +76,24 @@ class _BugListPageState extends State<BugListPage> {
'resolveTime': '2026-03-26', 'resolveTime': '2026-03-26',
'reportTime': '2026-03-18', 'reportTime': '2026-03-18',
'affectedUsers': '少数用户', 'affectedUsers': '少数用户',
'expanded': false, // 添加展开状态
}, },
]; ];
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
bool _isRefreshing = false; bool _isRefreshing = false;
// 切换bug展开状态
void _toggleBugExpanded(int bugId) {
setState(() {
final bugIndex = _bugs.indexWhere((bug) => bug['id'] == bugId);
if (bugIndex != -1) {
_bugs[bugIndex]['expanded'] = !_bugs[bugIndex]['expanded'];
}
});
HapticFeedback.lightImpact();
}
@override @override
void dispose() { void dispose() {
_scrollController.dispose(); _scrollController.dispose();
@@ -231,6 +247,8 @@ class _BugListPageState extends State<BugListPage> {
} }
Widget _buildBugItem(Map<String, dynamic> bug) { Widget _buildBugItem(Map<String, dynamic> bug) {
final bool isExpanded = bug['expanded'] ?? false;
return Container( return Container(
margin: const EdgeInsets.only(bottom: 16), margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -352,87 +370,116 @@ class _BugListPageState extends State<BugListPage> {
), ),
], ],
), ),
], const SizedBox(height: 12),
), // 解决方案按钮
), Container(
// 分割线 width: double.infinity,
const Divider(height: 1), child: ElevatedButton.icon(
// 解决方案区域 onPressed: () => _toggleBugExpanded(bug['id']),
Container( icon: Icon(
padding: const EdgeInsets.all(16), isExpanded ? Icons.expand_less : Icons.expand_more,
decoration: BoxDecoration( size: 18,
color: Colors.grey[50],
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(12),
bottomRight: Radius.circular(12),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.lightbulb_outline,
color: AppConstants.primaryColor,
size: 16,
), ),
const SizedBox(width: 8), label: Text(
Text( isExpanded ? '收起解决方案' : '查看解决方案',
'解决方案', style: const TextStyle(fontSize: 14),
style: TextStyle( ),
fontSize: 14, style: ElevatedButton.styleFrom(
fontWeight: FontWeight.w600, backgroundColor: AppConstants.primaryColor.withValues(alpha: 0.1),
color: AppConstants.primaryColor, foregroundColor: AppConstants.primaryColor,
elevation: 0,
padding: const EdgeInsets.symmetric(vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: AppConstants.primaryColor.withValues(alpha: 0.3),
),
), ),
), ),
],
),
const SizedBox(height: 8),
Text(
bug['solution'] ?? '暂无解决方案',
style: const TextStyle(
fontSize: 13,
color: Colors.black54,
height: 1.4,
), ),
), ),
const SizedBox(height: 12),
// 时间信息
Row(
children: [
Icon(
Icons.schedule,
size: 14,
color: Colors.grey[600],
),
const SizedBox(width: 4),
Text(
'预计解决: ${bug['resolveTime'] ?? '待定'}',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
const SizedBox(width: 16),
Icon(
Icons.report,
size: 14,
color: Colors.grey[600],
),
const SizedBox(width: 4),
Text(
'报告时间: ${bug['reportTime'] ?? '未知'}',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
], ],
), ),
), ),
// 解决方案区域(可展开/收起)
if (isExpanded) ...[
const Divider(height: 1),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(12),
bottomRight: Radius.circular(12),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.lightbulb_outline,
color: AppConstants.primaryColor,
size: 16,
),
const SizedBox(width: 8),
Text(
'解决方案',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppConstants.primaryColor,
),
),
],
),
const SizedBox(height: 8),
Text(
bug['solution'] ?? '暂无解决方案',
style: const TextStyle(
fontSize: 13,
color: Colors.black54,
height: 1.4,
),
),
const SizedBox(height: 12),
// 时间信息
Row(
children: [
Icon(
Icons.schedule,
size: 14,
color: Colors.grey[600],
),
const SizedBox(width: 4),
Text(
'预计解决: ${bug['resolveTime'] ?? '待定'}',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
const SizedBox(width: 16),
Icon(
Icons.report,
size: 14,
color: Colors.grey[600],
),
const SizedBox(width: 4),
Text(
'报告时间: ${bug['reportTime'] ?? '未知'}',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
],
),
),
],
], ],
), ),
); );

View File

@@ -6,6 +6,8 @@ import 'package:intl/intl.dart';
import '../../../constants/app_constants.dart'; import '../../../constants/app_constants.dart';
import '../../../controllers/sqlite_storage_controller.dart'; import '../../../controllers/sqlite_storage_controller.dart';
import '../../../controllers/history_controller.dart';
import '../../../services/network_listener_service.dart';
/// 时间: 2026-03-28 /// 时间: 2026-03-28
/// 功能: 答题记录页面 /// 功能: 答题记录页面
@@ -177,28 +179,68 @@ class _DistinguishPageState extends State<DistinguishPage> {
// 标题 // 标题
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Container( Row(
padding: const EdgeInsets.all(8), children: [
decoration: BoxDecoration( Container(
color: AppConstants.primaryColor.withAlpha(20), padding: const EdgeInsets.all(8),
borderRadius: BorderRadius.circular(10), decoration: BoxDecoration(
), color: AppConstants.primaryColor.withAlpha(20),
child: Icon( borderRadius: BorderRadius.circular(10),
Icons.analytics_outlined, ),
color: AppConstants.primaryColor, child: Icon(
size: 24, Icons.analytics_outlined,
), color: AppConstants.primaryColor,
size: 24,
),
),
const SizedBox(width: 12),
const Text(
'本次答题记录',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
],
), ),
const SizedBox(width: 12), // 复制内容按钮
const Text( if (_answerRecords.isNotEmpty)
'答题记录', Container(
style: TextStyle( decoration: BoxDecoration(
fontSize: 22, color: AppConstants.primaryColor.withAlpha(15),
fontWeight: FontWeight.bold, borderRadius: BorderRadius.circular(8),
color: Colors.black87, border: Border.all(
color: AppConstants.primaryColor.withAlpha(50),
width: 0.5,
),
),
child: TextButton.icon(
onPressed: _copyStatisticsContent,
icon: Icon(
Icons.copy,
size: 16,
color: Colors.orange[700],
),
label: Text(
'添加笔记',
style: TextStyle(
fontSize: 12,
color: Colors.orange[700],
),
),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
),
), ),
),
], ],
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
@@ -703,4 +745,153 @@ $_poetryLevel
), ),
); );
} }
// 写入统计数据到笔记
Future<void> _copyStatisticsContent() async {
try {
// 生成完整的评估报告内容
final StringBuffer content = StringBuffer();
content.writeln('🎓 诗词答题记录评估报告');
content.writeln('');
content.writeln('📊 基础数据统计');
content.writeln('━━━━━━━━━━━━━━━━');
content.writeln('• 已答题数:$_totalQuestions');
content.writeln('• 正确数量:$_correctAnswers');
content.writeln('• 错误数量:$_wrongAnswers');
content.writeln('• 正确率:${_correctRate.toStringAsFixed(1)}%');
content.writeln('• 错误率:${_wrongRate.toStringAsFixed(1)}%');
content.writeln('• 平均用时:${_averageTime.toStringAsFixed(1)} 秒/题');
content.writeln('');
content.writeln('📈 辅助数据');
content.writeln('━━━━━━━━━━━━━━━━');
content.writeln('• 提示次数:$_hintCount');
content.writeln('• 跳过次数:$_skipCount');
content.writeln('');
content.writeln('🏆 诗词水平评估');
content.writeln('━━━━━━━━━━━━━━━━');
content.writeln('$_poetryLevel');
content.writeln('');
content.writeln('💡 AI评估提示');
content.writeln('━━━━━━━━━━━━━━━━');
content.writeln('请根据以上答题数据,综合评估我的诗词水平,并给出:');
content.writeln('1. 当前诗词水平的详细分析');
content.writeln('2. 薄弱环节和改进建议');
content.writeln('3. 推荐学习的诗词类型或朝代');
content.writeln('4. 适合的诗词学习路径建议');
content.writeln('');
content.writeln('感谢您的评估!');
// 保存到笔记
final noteId = await HistoryController.saveNote(
title: '诗词答题评估_${DateFormat('yyyyMMdd_HHmmss').format(DateTime.now())}',
content: content.toString(),
category: '答题评估',
);
if (noteId != null) {
NetworkListenerService().sendSuccessEvent(
NetworkEventType.noteUpdate,
data: noteId,
);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('评估报告已保存到笔记'),
duration: Duration(seconds: 2),
),
);
}
}
} catch (e) {
debugPrint('保存笔记失败: $e');
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('保存笔记失败: $e')));
}
}
}
// 从答题记录创建笔记
Future<void> _createNoteFromRecords() async {
try {
// 生成答题记录的详细内容
final StringBuffer content = StringBuffer();
content.writeln('## 答题记录汇总');
content.writeln(
'生成时间: ${DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now())}',
);
content.writeln('总记录数: ${_answerRecords.length}');
content.writeln('');
// 统计信息
int correctCount = 0;
int totalCount = _answerRecords.length;
for (final record in _answerRecords) {
if (record['isCorrect'] == true) {
correctCount++;
}
}
content.writeln('### 统计信息');
content.writeln('- 总题数: $totalCount');
content.writeln('- 答对数: $correctCount');
content.writeln('- 答错数: ${totalCount - correctCount}');
content.writeln(
'- 正确率: ${totalCount > 0 ? (correctCount / totalCount * 100).toStringAsFixed(1) : 0}%',
);
content.writeln('');
content.writeln('### 详细记录');
content.writeln('');
for (int i = 0; i < _answerRecords.length; i++) {
final record = _answerRecords[i];
final question = record['question'] ?? '未知题目';
final userAnswer = record['userAnswer'] ?? '未知答案';
final correctAnswer = record['correctAnswer'] ?? '未知答案';
final isCorrect = record['isCorrect'] == true;
final answerTime = record['answerTime'] ?? '未知时间';
final tags = record['tags'] as List<dynamic>? ?? [];
content.writeln('#### ${i + 1}. $question');
content.writeln('- **你的答案**: $userAnswer');
content.writeln('- **正确答案**: $correctAnswer');
content.writeln('- **答题结果**: ${isCorrect ? '✅ 正确' : '❌ 错误'}');
content.writeln('- **答题时间**: ${_formatTime(answerTime)}');
if (tags.isNotEmpty) {
content.writeln('- **标签**: ${tags.join(', ')}');
}
content.writeln('');
}
final noteId = await HistoryController.saveNote(
title: '答题记录_${DateFormat('yyyyMMdd_HHmmss').format(DateTime.now())}',
content: content.toString(),
category: '答题记录',
);
if (noteId != null) {
NetworkListenerService().sendSuccessEvent(
NetworkEventType.noteUpdate,
data: noteId,
);
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('答题记录已保存到笔记')));
}
}
} catch (e) {
debugPrint('创建笔记失败: $e');
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('创建笔记失败: $e')));
}
}
}
} }

View File

@@ -25,10 +25,12 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
bool _darkModeEnabled = false; bool _darkModeEnabled = false;
bool _preloadEnabled = true; bool _preloadEnabled = true;
bool _notificationEnabled = true; bool _notificationEnabled = true;
bool _globalTipsEnabled = true; // 添加全局Tips开关状态
int _cacheSize = 128; int _cacheSize = 128;
static const String _autoRefreshKey = 'auto_refresh_enabled'; static const String _autoRefreshKey = 'auto_refresh_enabled';
static const String _debugInfoKey = 'debug_info_enabled'; static const String _debugInfoKey = 'debug_info_enabled';
static const String _globalTipsKey = 'global_tips_enabled'; // 添加全局Tips开关key
@override @override
void initState() { void initState() {
@@ -42,6 +44,8 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
setState(() { setState(() {
_autoRefreshEnabled = prefs.getBool(_autoRefreshKey) ?? false; _autoRefreshEnabled = prefs.getBool(_autoRefreshKey) ?? false;
_debugInfoEnabled = prefs.getBool(_debugInfoKey) ?? false; _debugInfoEnabled = prefs.getBool(_debugInfoKey) ?? false;
_globalTipsEnabled =
prefs.getBool(_globalTipsKey) ?? true; // 加载全局Tips开关状态
_preloadEnabled = prefs.getBool('preload_enabled') ?? true; _preloadEnabled = prefs.getBool('preload_enabled') ?? true;
}); });
} }
@@ -78,6 +82,17 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
} }
} }
// 设置全局Tips开关
Future<void> _setGlobalTips(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_globalTipsKey, value);
if (mounted) {
setState(() {
_globalTipsEnabled = value;
});
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@@ -143,8 +158,8 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
'全局Tips开关', '全局Tips开关',
'显示一些使用技巧', '显示一些使用技巧',
Icons.volume_up, Icons.volume_up,
_soundEnabled, _globalTipsEnabled,
(value) => setState(() => _soundEnabled = value), (value) => _setGlobalTips(value),
), ),
_buildSwitchItem( _buildSwitchItem(
'声音反馈', '声音反馈',
@@ -420,6 +435,7 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
setState(() { setState(() {
_soundEnabled = true; _soundEnabled = true;
_vibrationEnabled = true; _vibrationEnabled = true;
_globalTipsEnabled = true; // 重置全局Tips开关为开启
_darkModeEnabled = false; _darkModeEnabled = false;
_notificationEnabled = true; _notificationEnabled = true;
}); });

View File

@@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <audioplayers_linux/audioplayers_linux_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
} }

View File

@@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_linux
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@@ -5,10 +5,14 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import audioplayers_darwin
import device_info_plus import device_info_plus
import path_provider_foundation
import shared_preferences_foundation import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
} }

View File

@@ -5,10 +5,82 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: async name: async
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.13.0" version: "2.13.1"
audioplayers:
dependency: "direct main"
description:
path: "packages/audioplayers"
ref: HEAD
resolved-ref: e566d678531badc1437ada9fcca87c23d6355ba4
url: "https://gitcode.com/openharmony-sig/flutter_audioplayers.git"
source: git
version: "4.1.0"
audioplayers_android:
dependency: transitive
description:
path: "packages/audioplayers_android"
ref: HEAD
resolved-ref: e566d678531badc1437ada9fcca87c23d6355ba4
url: "https://gitcode.com/openharmony-sig/flutter_audioplayers.git"
source: git
version: "3.0.2"
audioplayers_darwin:
dependency: transitive
description:
path: "packages/audioplayers_darwin"
ref: HEAD
resolved-ref: e566d678531badc1437ada9fcca87c23d6355ba4
url: "https://gitcode.com/openharmony-sig/flutter_audioplayers.git"
source: git
version: "4.1.0"
audioplayers_linux:
dependency: transitive
description:
path: "packages/audioplayers_linux"
ref: HEAD
resolved-ref: e566d678531badc1437ada9fcca87c23d6355ba4
url: "https://gitcode.com/openharmony-sig/flutter_audioplayers.git"
source: git
version: "2.1.0"
audioplayers_ohos:
dependency: transitive
description:
path: "packages/audioplayers_ohos"
ref: HEAD
resolved-ref: e566d678531badc1437ada9fcca87c23d6355ba4
url: "https://gitcode.com/openharmony-sig/flutter_audioplayers.git"
source: git
version: "3.0.2"
audioplayers_platform_interface:
dependency: transitive
description:
path: "packages/audioplayers_platform_interface"
ref: HEAD
resolved-ref: e566d678531badc1437ada9fcca87c23d6355ba4
url: "https://gitcode.com/openharmony-sig/flutter_audioplayers.git"
source: git
version: "5.0.1"
audioplayers_web:
dependency: transitive
description:
path: "packages/audioplayers_web"
ref: HEAD
resolved-ref: e566d678531badc1437ada9fcca87c23d6355ba4
url: "https://gitcode.com/openharmony-sig/flutter_audioplayers.git"
source: git
version: "3.1.0"
audioplayers_windows:
dependency: transitive
description:
path: "packages/audioplayers_windows"
ref: HEAD
resolved-ref: e566d678531badc1437ada9fcca87c23d6355ba4
url: "https://gitcode.com/openharmony-sig/flutter_audioplayers.git"
source: git
version: "2.0.2"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@@ -49,6 +121,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "4.0.9" version: "4.0.9"
crypto:
dependency: transitive
description:
name: crypto
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.7"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -61,10 +141,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: device_info_plus name: device_info_plus
sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a" sha256: "72d146c6d7098689ff5c5f66bcf593ac11efc530095385356e131070333e64da"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "11.5.0" version: "11.3.0"
device_info_plus_platform_interface: device_info_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -117,10 +197,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: file name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "7.0.1" version: "6.1.4"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@@ -144,6 +224,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
http:
dependency: transitive
description:
name: http
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.13.6"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
@@ -160,6 +248,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.20.2" version: "0.20.2"
js:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.6.7"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@@ -232,6 +328,31 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.9.1" version: "1.9.1"
path_provider:
dependency: transitive
description:
path: "packages/path_provider/path_provider"
ref: HEAD
resolved-ref: b7d7f892e446d198c596487fda3c57d8e054f9e0
url: "https://gitcode.com/openharmony-sig/flutter_packages.git"
source: git
version: "2.1.0"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.23"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.5.1"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@@ -240,6 +361,15 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.2.1" version: "2.2.1"
path_provider_ohos:
dependency: transitive
description:
path: "packages/path_provider/path_provider_ohos"
ref: HEAD
resolved-ref: b7d7f892e446d198c596487fda3c57d8e054f9e0
url: "https://gitcode.com/openharmony-tpc/flutter_packages.git"
source: git
version: "2.2.1"
path_provider_platform_interface: path_provider_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -383,6 +513,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.4.1" version: "1.4.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.4.0"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@@ -415,6 +553,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.3.1" version: "2.3.1"
uuid:
dependency: transitive
description:
name: uuid
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.7"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@@ -467,10 +613,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32_registry name: win32_registry
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae" sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.0" version: "1.1.5"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:

View File

@@ -42,6 +42,12 @@ dependencies:
platform_info: ^5.0.0 platform_info: ^5.0.0
vibration: ^2.0.0 #删除 vibration: ^2.0.0 #删除
intl: ^0.20.2 intl: ^0.20.2
audioplayers:
git:
url: https://gitcode.com/openharmony-sig/flutter_audioplayers.git
path: packages/audioplayers
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@@ -67,9 +73,8 @@ flutter:
uses-material-design: true uses-material-design: true
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
# assets: assets:
# - images/a_dot_burr.jpeg - assets/audios/deep.mp3
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images # https://flutter.dev/to/resolution-aware-images

View File

@@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <audioplayers_windows/audioplayers_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
AudioplayersWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
} }

View File

@@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_windows
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST