From 4cd7629f61f67186d25d58fbf6e574d531edcbd5 Mon Sep 17 00:00:00 2001 From: Developer Date: Mon, 30 Mar 2026 21:30:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A3=B0=E9=9F=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 18 ++ assets/audios/deep.mp3 | Bin 0 -> 1584 bytes lib/utils/audio_manager.dart | 97 ++++++++ lib/views/home/home_page.dart | 12 + lib/views/home/home_part.dart | 74 ++++-- .../profile/components/bug_list_page.dart | 193 +++++++++------ lib/views/profile/level/distinguish.dart | 229 ++++++++++++++++-- lib/views/profile/settings/app_fun.dart | 20 +- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 4 + pubspec.lock | 162 ++++++++++++- pubspec.yaml | 11 +- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 15 files changed, 703 insertions(+), 126 deletions(-) create mode 100644 assets/audios/deep.mp3 create mode 100644 lib/utils/audio_manager.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index fb36f83..ec6eadd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 ### 新增 diff --git a/assets/audios/deep.mp3 b/assets/audios/deep.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..cc8fb4cdfba4cbb044c57c8fd1bb6a9a09498ad5 GIT binary patch literal 1584 zcmZ9MdpHwn9LL|yCTvW#GAX%Ln%fZN=;|_e5wcv8+gUcI6r$&3=C(4Y(I}-uii>462RE-lo+Ji#UlR`m5uA1A zHPEQuzL~EI2&Q1Z8Q3!cO5YYrhGFq{4*WTjcvd_Urf1QibHa18DGr*h5Y7uP<7Uyv zAQY#-?`gcRDX_bk`|S>#D~$jf58;jrUo;}v6vi>543boFCia8ayW8ZlTN!!H8-1de z3MZJmc8gQdt&6n;MQ4{=3Z^Y5BGa_>>@=A>E{-*?Z0q&P8!EhTq2=&fS0`GeO6|j< z&P9!hPeT-8EAO{w!ObDe>6bSBp#ceje#^>n6?Iwqm|_QW5tY7#H^%+9(|@qb{S zYYywrv|JL5v>N*#wQh`-P$#wOV*=@eS~V+!scKsrU4QydPWRVHvV5O~b-IsZ>5je1 z2!KX>XXd7NV7D_`&B~kfHf`cfmHRyNTPZ%B1wD!nirB!O(HS!crE|3k=dBrI_63W; zRI=Pff095Tp1BlAc1Sbjs(}B{} z;v}B8dtkSi!pQ8w_Z9SAx-_phXUJZPyEe2~Uv{(l=!_)TmaFgS#UYC@+gVxT9+ui4 zEiY%R$wi#Zv?kog`5*4`udB0H$}7uD^1f&>b@vEAv;vwXKdd+mjZl(xFJ!2AP1xk3)ZO-L z*m>CP{tFX5G1p$Of3J0-Zsbtd-D+bWElQ^9=mx&YG6Q>}w?ivfot?>!FM5-G>Yb*p z#>Q#KWS@a?>a89^A--X~?gg)1AL?B;ya+m$o?jg&;0x7^;)o#&{f{vkJNB1*2w2?I zM@k5D;x^;@d$w4D*mz;H~@@5c%W1-G|Z~_X63yE@O)}f zkA3qi)6*ld4R~6*I`a2-F%M_@EPWjw>m_9t;1l>f*~U!E@I~gDhh-EtaK7|2Jo#q50$+=PhSy#Bdw*4po7`mLDG`;OT?!dx zv4*A})K1cNKj%3G-fk(Y5JncR6|0}1r#mt~IJ2*AL8UQE7Iu)knvtn+E+RrfZI{SF zNNP!t=Hg{tV`M#;#5X$AsEaKL^(hl)``cNV@0o24q|l>YXTO|H}}GJXJ}cEuEkV~DidsSUs>&Em2i){5|cvL>RR3y*6Q9=&y)8Q`XwI>c{R38&(@sE#js*aK_$u>3n-?c=(MXZ; zXO%|wJSASa@zv?S#^O%}xrpCQT@-EKyf^m9jy&3BQgn7ns_NT&ciSq2%pyaTjPNnK z+Rjnt)e#fd+mak-!@rHIo>m#Fq7fxgOCd{dEsCK81K~e@R_<^PAAtnw$Z>#@B_ecy J5&+VA{uktQ;W7XK literal 0 HcmV?d00001 diff --git a/lib/utils/audio_manager.dart b/lib/utils/audio_manager.dart new file mode 100644 index 0000000..fc4a93d --- /dev/null +++ b/lib/utils/audio_manager.dart @@ -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 init() async { + if (_isInitialized) return; + + try { + // 设置音频播放器模式 + await _audioPlayer.setPlayerMode(PlayerMode.lowLatency); + _isInitialized = true; + _debugLog('音频管理器初始化成功'); + } catch (e) { + _debugLog('音频管理器初始化失败: $e'); + } + } + + /// 播放点击音效 + Future playClickSound() async { + if (_isMuted) return; + await _playSound('audios/deep.mp3'); + } + + /// 播放点赞音效 + Future playLikeSound() async { + if (_isMuted) return; + await _playSound('audios/deep.mp3'); + } + + /// 播放下一条音效 + Future playNextSound() async { + if (_isMuted) return; + await _playSound('audios/deep.mp3'); + } + + /// 通用播放方法 + Future _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 dispose() async { + try { + await _audioPlayer.dispose(); + _isInitialized = false; + _debugLog('音频管理器已释放'); + } catch (e) { + _debugLog('释放音频管理器失败: $e'); + } + } + + void _debugLog(String message) { + if (kDebugMode) { + print('[AudioManager] $message'); + } + } +} diff --git a/lib/views/home/home_page.dart b/lib/views/home/home_page.dart index 4e2835c..6b4f38a 100644 --- a/lib/views/home/home_page.dart +++ b/lib/views/home/home_page.dart @@ -10,6 +10,7 @@ import '../../constants/app_constants.dart'; import '../../../controllers/history_controller.dart'; import '../../../utils/http/poetry_api.dart'; import '../../../services/network_listener_service.dart'; +import '../../../utils/audio_manager.dart'; import 'home_part.dart'; import 'home_components.dart'; import 'home-load.dart'; @@ -57,12 +58,17 @@ class _HomePageState extends State _initAutoRefresh(); _initDebugInfo(); _initOfflineDataManager(); + _initAudioManager(); // 延迟加载诗词,确保页面先显示 WidgetsBinding.instance.addPostFrameCallback((_) { _loadPoetry(); }); } + Future _initAudioManager() async { + await AudioManager().init(); + } + Future _initOfflineDataManager() async { final offlineDataManager = OfflineDataManager(); await offlineDataManager.init(); @@ -287,6 +293,9 @@ class _HomePageState extends State Future _toggleLike() async { if (_poetryData == null || _isLoadingLike) return; + // 播放点赞音效 + await AudioManager().playLikeSound(); + // 立即切换按钮状态和显示加载 setState(() { _isLoadingLike = true; @@ -425,6 +434,9 @@ class _HomePageState extends State void _loadNextPoetry() async { if (_isLoadingNext) return; + // 播放下一条音效 + await AudioManager().playNextSound(); + setState(() { _isLoadingNext = true; // 设置所有区域为加载状态 diff --git a/lib/views/home/home_part.dart b/lib/views/home/home_part.dart index 4475800..0802531 100644 --- a/lib/views/home/home_part.dart +++ b/lib/views/home/home_part.dart @@ -5,8 +5,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import '../../../constants/app_constants.dart'; import '../../../utils/http/poetry_api.dart'; +import '../../../utils/flutter_compatibility_fix.dart'; +import '../../../utils/audio_manager.dart'; import 'home_components.dart'; /// 诗词卡片组件 - 优化版本,防止拉伸和处理文本溢出 @@ -31,6 +34,13 @@ class PoetryCard extends StatefulWidget { class _PoetryCardState extends State { bool _showCopyTip = true; bool _showRecommendation = false; + bool _globalTipsEnabled = true; // 添加全局Tips开关状态 + + @override + void initState() { + super.initState(); + _loadGlobalTipsSettings(); // 加载全局Tips设置 + } String _getTimeOfDayGreeting() { final hour = DateTime.now().hour; @@ -74,10 +84,34 @@ class _PoetryCardState extends State { } } + // 加载全局Tips设置 + Future _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 Widget build(BuildContext context) { return GestureDetector( - onTap: widget.onTap, + onTap: () async { + // 播放点击音效 + await AudioManager().playClickSound(); + // 调用原始的onTap回调 + widget.onTap?.call(); + }, child: Stack( children: [ Container( @@ -87,7 +121,7 @@ class _PoetryCardState extends State { borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( - color: Colors.black.withValues(alpha: 0.1), + color: Colors.black.withAlpha(10), blurRadius: 10, offset: const Offset(0, 2), ), @@ -143,7 +177,7 @@ class _PoetryCardState extends State { decoration: BoxDecoration( gradient: LinearGradient( colors: [ - AppConstants.primaryColor.withValues(alpha: 0.8), + AppConstants.primaryColor.withAlpha(204), AppConstants.primaryColor, ], begin: Alignment.topLeft, @@ -197,7 +231,7 @@ class _PoetryCardState extends State { borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( - color: AppConstants.primaryColor.withValues(alpha: 0.3), + color: AppConstants.primaryColor.withAlpha(76), blurRadius: 8, offset: const Offset(0, 2), ), @@ -208,8 +242,8 @@ class _PoetryCardState extends State { children: [ const Icon(Icons.info_outline, color: Colors.white, size: 16), const SizedBox(width: 6), - const Text( - '点击任意区域加载下一条,长按复制,下拉刷新', + Text( + _globalTipsEnabled ? '点击任意区域加载下一条,长按复制,下拉刷新' : '', style: TextStyle( color: Colors.white, fontSize: 12, @@ -293,20 +327,20 @@ class _PoetryCardState extends State { decoration: BoxDecoration( gradient: LinearGradient( colors: [ - AppConstants.primaryColor.withValues(alpha: 0.1), - AppConstants.primaryColor.withValues(alpha: 0.05), + AppConstants.primaryColor.withAlpha(26), + AppConstants.primaryColor.withAlpha(13), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(12), border: Border.all( - color: AppConstants.primaryColor.withValues(alpha: 0.2), + color: AppConstants.primaryColor.withAlpha(51), width: 1, ), boxShadow: [ BoxShadow( - color: AppConstants.primaryColor.withValues(alpha: 0.1), + color: AppConstants.primaryColor.withAlpha(26), blurRadius: 8, offset: const Offset(0, 2), ), @@ -437,7 +471,7 @@ class _PoetryCardState extends State { ), decoration: BoxDecoration( color: AppConstants.secondaryColor - .withValues(alpha: 0.1), + .withAlpha(26), borderRadius: BorderRadius.circular(8), ), child: Text( @@ -468,8 +502,8 @@ class _PoetryCardState extends State { vertical: 4, ), decoration: BoxDecoration( - color: AppConstants.primaryColor.withValues( - alpha: 0.1, + color: AppConstants.primaryColor.withAlpha( + 26, ), borderRadius: BorderRadius.circular(12), ), @@ -604,11 +638,9 @@ class _PoetryCardState extends State { constraints: const BoxConstraints(maxHeight: 150), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppConstants.infoColor.withValues(alpha: 0.05), + color: AppConstants.infoColor.withAlpha(13), borderRadius: BorderRadius.circular(12), - border: Border.all( - color: AppConstants.infoColor.withValues(alpha: 0.2), - ), + border: Border.all(color: AppConstants.infoColor.withAlpha(51)), ), child: isLoading ? Center( @@ -671,7 +703,7 @@ class FloatingPreviousButton extends StatelessWidget { shape: BoxShape.circle, boxShadow: [ BoxShadow( - color: AppConstants.secondaryColor.withValues(alpha: 0.3), + color: AppConstants.secondaryColor.withAlpha(76), blurRadius: 8, offset: const Offset(0, 4), ), @@ -711,7 +743,7 @@ class FloatingNextButton extends StatelessWidget { shape: BoxShape.circle, boxShadow: [ BoxShadow( - color: AppConstants.primaryColor.withValues(alpha: 0.3), + color: AppConstants.primaryColor.withAlpha(76), blurRadius: 8, offset: const Offset(0, 4), ), @@ -759,7 +791,7 @@ class FloatingLikeButton extends StatelessWidget { BoxShadow( color: (isLiked ? AppConstants.errorColor : AppConstants.primaryColor) - .withValues(alpha: 0.3), + .withAlpha(76), blurRadius: 8, offset: const Offset(0, 4), ), @@ -813,7 +845,7 @@ class StatsCard extends StatelessWidget { borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: Colors.black.withValues(alpha: 0.05), + color: Colors.black.withAlpha(5), blurRadius: 5, offset: const Offset(0, 1), ), diff --git a/lib/views/profile/components/bug_list_page.dart b/lib/views/profile/components/bug_list_page.dart index 8b924ec..1674835 100644 --- a/lib/views/profile/components/bug_list_page.dart +++ b/lib/views/profile/components/bug_list_page.dart @@ -28,6 +28,7 @@ class _BugListPageState extends State { 'resolveTime': '2026-04-15', 'reportTime': '2026-03-25', 'affectedUsers': '部分用户', + 'expanded': false, // 添加展开状态 }, { 'id': 2, @@ -39,6 +40,7 @@ class _BugListPageState extends State { 'resolveTime': '2026-04-10', 'reportTime': '2026-03-20', 'affectedUsers': '大量用户', + 'expanded': false, // 添加展开状态 }, { 'id': 3, @@ -50,6 +52,7 @@ class _BugListPageState extends State { 'resolveTime': '2026-03-28', 'reportTime': '2026-03-15', 'affectedUsers': '少数用户', + 'expanded': false, // 添加展开状态 }, { 'id': 4, @@ -61,6 +64,7 @@ class _BugListPageState extends State { 'resolveTime': '2026-04-20', 'reportTime': '2026-03-22', 'affectedUsers': '部分用户', + 'expanded': false, // 添加展开状态 }, { 'id': 5, @@ -72,12 +76,24 @@ class _BugListPageState extends State { 'resolveTime': '2026-03-26', 'reportTime': '2026-03-18', 'affectedUsers': '少数用户', + 'expanded': false, // 添加展开状态 }, ]; final ScrollController _scrollController = ScrollController(); 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 void dispose() { _scrollController.dispose(); @@ -231,6 +247,8 @@ class _BugListPageState extends State { } Widget _buildBugItem(Map bug) { + final bool isExpanded = bug['expanded'] ?? false; + return Container( margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( @@ -352,87 +370,116 @@ class _BugListPageState extends State { ), ], ), - ], - ), - ), - // 分割线 - 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(height: 12), + // 解决方案按钮 + Container( + width: double.infinity, + child: ElevatedButton.icon( + onPressed: () => _toggleBugExpanded(bug['id']), + icon: Icon( + isExpanded ? Icons.expand_less : Icons.expand_more, + size: 18, ), - const SizedBox(width: 8), - Text( - '解决方案', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: AppConstants.primaryColor, + label: Text( + isExpanded ? '收起解决方案' : '查看解决方案', + style: const TextStyle(fontSize: 14), + ), + style: ElevatedButton.styleFrom( + backgroundColor: AppConstants.primaryColor.withValues(alpha: 0.1), + 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], + ), + ), + ], + ), + ], + ), + ), + ], ], ), ); diff --git a/lib/views/profile/level/distinguish.dart b/lib/views/profile/level/distinguish.dart index 31fe3fc..f00e4e8 100644 --- a/lib/views/profile/level/distinguish.dart +++ b/lib/views/profile/level/distinguish.dart @@ -6,6 +6,8 @@ import 'package:intl/intl.dart'; import '../../../constants/app_constants.dart'; import '../../../controllers/sqlite_storage_controller.dart'; +import '../../../controllers/history_controller.dart'; +import '../../../services/network_listener_service.dart'; /// 时间: 2026-03-28 /// 功能: 答题记录页面 @@ -177,28 +179,68 @@ class _DistinguishPageState extends State { // 标题 Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: AppConstants.primaryColor.withAlpha(20), - borderRadius: BorderRadius.circular(10), - ), - child: Icon( - Icons.analytics_outlined, - color: AppConstants.primaryColor, - size: 24, - ), + Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: AppConstants.primaryColor.withAlpha(20), + borderRadius: BorderRadius.circular(10), + ), + child: Icon( + 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( - '答题记录', - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: Colors.black87, + // 复制内容按钮 + if (_answerRecords.isNotEmpty) + Container( + decoration: BoxDecoration( + color: AppConstants.primaryColor.withAlpha(15), + borderRadius: BorderRadius.circular(8), + 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), @@ -703,4 +745,153 @@ $_poetryLevel ), ); } + + // 写入统计数据到笔记 + Future _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 _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? ?? []; + + 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'))); + } + } + } } diff --git a/lib/views/profile/settings/app_fun.dart b/lib/views/profile/settings/app_fun.dart index 6b0e038..3d417d2 100644 --- a/lib/views/profile/settings/app_fun.dart +++ b/lib/views/profile/settings/app_fun.dart @@ -25,10 +25,12 @@ class _AppFunSettingsPageState extends State { bool _darkModeEnabled = false; bool _preloadEnabled = true; bool _notificationEnabled = true; + bool _globalTipsEnabled = true; // 添加全局Tips开关状态 int _cacheSize = 128; static const String _autoRefreshKey = 'auto_refresh_enabled'; static const String _debugInfoKey = 'debug_info_enabled'; + static const String _globalTipsKey = 'global_tips_enabled'; // 添加全局Tips开关key @override void initState() { @@ -42,6 +44,8 @@ class _AppFunSettingsPageState extends State { setState(() { _autoRefreshEnabled = prefs.getBool(_autoRefreshKey) ?? false; _debugInfoEnabled = prefs.getBool(_debugInfoKey) ?? false; + _globalTipsEnabled = + prefs.getBool(_globalTipsKey) ?? true; // 加载全局Tips开关状态 _preloadEnabled = prefs.getBool('preload_enabled') ?? true; }); } @@ -78,6 +82,17 @@ class _AppFunSettingsPageState extends State { } } + // 设置全局Tips开关 + Future _setGlobalTips(bool value) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(_globalTipsKey, value); + if (mounted) { + setState(() { + _globalTipsEnabled = value; + }); + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -143,8 +158,8 @@ class _AppFunSettingsPageState extends State { '全局Tips开关', '显示一些使用技巧', Icons.volume_up, - _soundEnabled, - (value) => setState(() => _soundEnabled = value), + _globalTipsEnabled, + (value) => _setGlobalTips(value), ), _buildSwitchItem( '声音反馈', @@ -420,6 +435,7 @@ class _AppFunSettingsPageState extends State { setState(() { _soundEnabled = true; _vibrationEnabled = true; + _globalTipsEnabled = true; // 重置全局Tips开关为开启 _darkModeEnabled = false; _notificationEnabled = true; }); diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..1830e5c 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include 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); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..e9abb91 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + audioplayers_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8d4d4ca..95e82b8 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,10 +5,14 @@ import FlutterMacOS import Foundation +import audioplayers_darwin import device_info_plus +import path_provider_foundation import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 1e02498..6b2630d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,82 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 url: "https://pub.flutter-io.cn" 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: dependency: transitive description: @@ -49,6 +121,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: "direct main" description: @@ -61,10 +141,10 @@ packages: dependency: transitive description: name: device_info_plus - sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a" + sha256: "72d146c6d7098689ff5c5f66bcf593ac11efc530095385356e131070333e64da" url: "https://pub.flutter-io.cn" source: hosted - version: "11.5.0" + version: "11.3.0" device_info_plus_platform_interface: dependency: transitive description: @@ -117,10 +197,10 @@ packages: dependency: transitive description: name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" url: "https://pub.flutter-io.cn" source: hosted - version: "7.0.1" + version: "6.1.4" flutter: dependency: "direct main" description: flutter @@ -144,6 +224,14 @@ packages: description: flutter source: sdk 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: dependency: transitive description: @@ -160,6 +248,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: transitive description: @@ -232,6 +328,31 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: transitive description: @@ -240,6 +361,15 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: transitive description: @@ -383,6 +513,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: transitive description: @@ -415,6 +553,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: transitive description: @@ -467,10 +613,10 @@ packages: dependency: transitive description: name: win32_registry - sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae" + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "1.1.5" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 174b33e..852c831 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,12 @@ dependencies: platform_info: ^5.0.0 vibration: ^2.0.0 #删除 intl: ^0.20.2 + audioplayers: + git: + url: https://gitcode.com/openharmony-sig/flutter_audioplayers.git + path: packages/audioplayers + + dev_dependencies: flutter_test: @@ -67,9 +73,8 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + assets: + - assets/audios/deep.mp3 # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/to/resolution-aware-images diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..09e8e2c 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + AudioplayersWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..375535c 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + audioplayers_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST