diff --git a/assets/sounds/correct_voice.mp3 b/assets/sounds/correct_voice.mp3 new file mode 100644 index 0000000..6aa429c --- /dev/null +++ b/assets/sounds/correct_voice.mp3 diff --git a/assets/sounds/incorrect_voice.mp3 b/assets/sounds/incorrect_voice.mp3 new file mode 100644 index 0000000..734ba1d --- /dev/null +++ b/assets/sounds/incorrect_voice.mp3 diff --git a/lib/common/extension/string_extension.dart b/lib/common/extension/string_extension.dart index d5462e0..c0e7c38 100644 --- a/lib/common/extension/string_extension.dart +++ b/lib/common/extension/string_extension.dart @@ -1,6 +1,7 @@ /// 资源类扩展方法 extension AssetExtension on String { static const String _assetImagePrefix = "assets/images/"; + static const String _assetSoundPrefix = 'sounds/'; /// 图片url String get assetImg => _assetImagePrefix + this; @@ -10,6 +11,10 @@ extension AssetExtension on String { String get assetWebp => '$assetImg.webp'; String get assetGif => '$assetImg.gif'; + + String get assetSound => _assetSoundPrefix + this; + + String get assetMp3 => '$assetSound.mp3'; } extension StringExtension on String { diff --git a/lib/pages/practice/bloc/topic_picture_bloc.dart b/lib/pages/practice/bloc/topic_picture_bloc.dart index 384c477..807cc9c 100644 --- a/lib/pages/practice/bloc/topic_picture_bloc.dart +++ b/lib/pages/practice/bloc/topic_picture_bloc.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/common/request/dao/listen_dao.dart'; import 'package:wow_english/common/request/exception.dart'; import 'package:wow_english/models/course_process_entity.dart'; @@ -45,6 +46,16 @@ class TopicPictureBloc extends Bloc { ///正在播放音频 VoicePlayState _voicePlayState = VoicePlayState.unKnow; + // 是否是回答(选择)结果音效 + bool _isResultSoundPlaying = false; + + bool get isResultSoundPlaying => _isResultSoundPlaying; + + // 答对播放音效时禁止任何点击(选择)操作 + bool _forbiddenWhenCorrect = false; + + bool get forbiddenWhenCorrect => _forbiddenWhenCorrect; + CourseProcessEntity? get entity => _entity; int get currentPage => _currentPage + 1; @@ -70,29 +81,43 @@ class TopicPictureBloc extends Bloc { on(_requestData); on(_voiceXsTest); on(_voiceXsStop); - on(_voicePlay); + on(_questionVoicePlay); on((event, emit) { //音频播放器 audioPlayer = AudioPlayer(); audioPlayer.onPlayerStateChanged.listen((event) async { - debugPrint('播放状态变化'); - if (event == PlayerState.completed) { - debugPrint('播放完成'); - _voicePlayState = VoicePlayState.completed; - } - if (event == PlayerState.stopped) { - debugPrint('播放结束'); - _voicePlayState = VoicePlayState.stop; - } + debugPrint('播放状态变化 _voicePlayState=$_voicePlayState event=$event _isResultSoundPlaying=$_isResultSoundPlaying _forbiddenWhenCorrect=$_forbiddenWhenCorrect'); + if (_isResultSoundPlaying) { + if (event != PlayerState.playing) { + _isResultSoundPlaying = false; + if (_forbiddenWhenCorrect) { + _forbiddenWhenCorrect = false; + // 答对后自动翻页 + pageController.nextPage( + duration: const Duration(milliseconds: 500), + curve: Curves.ease, + ); + } + } + } else { + if (event == PlayerState.completed) { + debugPrint('播放完成'); + _voicePlayState = VoicePlayState.completed; + } + if (event == PlayerState.stopped) { + debugPrint('播放结束'); + _voicePlayState = VoicePlayState.stop; + } - if (event == PlayerState.playing) { - debugPrint('正在播放中'); - _voicePlayState = VoicePlayState.playing; - } - if(isClosed) { - return; + if (event == PlayerState.playing) { + debugPrint('正在播放中'); + _voicePlayState = VoicePlayState.playing; + } + if (isClosed) { + return; + } + add(VoicePlayStateChangeEvent()); } - add(VoicePlayStateChangeEvent()); }); methodChannel = const MethodChannel('wow_english/sing_sound_method_channel'); @@ -129,6 +154,8 @@ class TopicPictureBloc extends Bloc { pageController.dispose(); audioPlayer.release(); audioPlayer.dispose(); + _isResultSoundPlaying = false; + _forbiddenWhenCorrect = false; _voiceXsCancel(); return super.close(); } @@ -149,10 +176,12 @@ class TopicPictureBloc extends Bloc { ///页面切换 void _pageControllerChange(CurrentPageIndexChangeEvent event,Emitter emitter) async { - _currentPage = event.pageIndex; - if (voicePlayState == VoicePlayState.playing) { - await audioPlayer.stop(); + await closePlayerResource(); + debugPrint('翻页 $_currentPage->${event.pageIndex}'); + if (_currentPage == _entity?.topics?.length) { + return; } + _currentPage = event.pageIndex; final topics = _entity?.topics?[_currentPage]; if (topics?.type != 3 && topics?.type != 4) { if (topics?.audioUrl != null) { @@ -169,13 +198,18 @@ class TopicPictureBloc extends Bloc { ///选择 void _selectItemLoad(SelectItemEvent event,Emitter emitter) async { + if (_forbiddenWhenCorrect) { + return; + } _selectItem = event.selectIndex; CourseProcessTopics? topics = _entity?.topics?[_currentPage]; CourseProcessTopicsTopicAnswerList? answerList = topics?.topicAnswerList?[_selectItem]; if (answerList?.correct == 0) { - showToast('继续加油哦',duration: const Duration(seconds: 2)); + _playResultSound(false); + // showToast('继续加油哦',duration: const Duration(seconds: 2)); } else { - showToast('恭喜你,答对啦!',duration: const Duration(seconds: 2)); + _playResultSound(true); + // showToast('恭喜你,答对啦!',duration: const Duration(seconds: 2)); } emitter(SelectItemChangeState()); } @@ -228,17 +262,37 @@ class TopicPictureBloc extends Bloc { emitter(XSVoiceTestState()); } + // 暂时没用上 void _voicePlayStateChange(VoicePlayStateChangeEvent event,Emitter emitter) async { emitter(VoicePlayStateChange()); } - void _voicePlay(VoicePlayEvent event,Emitter emitter) async { - if (voicePlayState == VoicePlayState.playing) { - await audioPlayer.stop(); + // 题目音频播放 + void _questionVoicePlay(VoicePlayEvent event,Emitter emitter) async { + if (_forbiddenWhenCorrect) { return; } + _forbiddenWhenCorrect = false; + await closePlayerResource(); final topics = _entity?.topics?[_currentPage]; final urlStr = topics?.audioUrl??''; await audioPlayer.play(UrlSource(urlStr)); } + + Future closePlayerResource() async { + if (voicePlayState == VoicePlayState.playing || _isResultSoundPlaying) { + await audioPlayer.stop(); + } + } + + void _playResultSound(bool isCorrect) async { + await audioPlayer.stop(); + _isResultSoundPlaying = true; + _forbiddenWhenCorrect = isCorrect; + if (isCorrect) { + await audioPlayer.play(AssetSource('correct_voice'.assetMp3)); + } else { + await audioPlayer.play(AssetSource('incorrect_voice'.assetMp3)); + } + } } diff --git a/lib/pages/practice/bloc/topic_picture_event.dart b/lib/pages/practice/bloc/topic_picture_event.dart index f69ef33..965fed5 100644 --- a/lib/pages/practice/bloc/topic_picture_event.dart +++ b/lib/pages/practice/bloc/topic_picture_event.dart @@ -48,5 +48,5 @@ class SelectItemEvent extends TopicPictureEvent { ///音频播放事件 class VoicePlayChangeEvent extends TopicPictureEvent {} -///播放音乐 +///播放(题目)音乐 class VoicePlayEvent extends TopicPictureEvent {} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 61ca7e4..a9f6c07 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -94,15 +94,15 @@ dependencies: # UI适配 https://pub.dev/packages/responsive_framework responsive_framework: ^1.0.0 # 音频播放 https://pub.dev/packages/audioplayers - audioplayers: ^4.1.0 + audioplayers: ^6.0.0 # 语音录制 https://pub.dev/packages/flutter_sound flutter_sound: ^9.2.13 # 音频播放 https://pub.dev/packages/audio_session - audio_session: ^0.1.16 + audio_session: ^0.1.19 # 文件管理 https://pub.dev/packages/path_provider path_provider: ^2.0.15 # 阿里云oss https://pub.dev/packages/flutter_oss_aliyun - flutter_oss_aliyun: ^6.2.7 + flutter_oss_aliyun: ^6.4.2 # App信息 https://pub.dev/packages/package_info_plus package_info_plus: ^4.2.0 # 应用内更新 https://pub-web.flutter-io.cn/packages/flutter_app_update @@ -135,6 +135,7 @@ flutter: assets: - assets/images/ - assets/fonts/ + - assets/sounds/ fonts: - family: HannotateSC fonts: