Commit aeafd474a56ea23a9a1d1b5e121aab9cd5031360

Authored by 吴启风
1 parent 8fec4713

feat:选择题作答后播放音效&答对自动翻页

assets/sounds/correct_voice.mp3 0 → 100644
No preview for this file type
assets/sounds/incorrect_voice.mp3 0 → 100644
No preview for this file type
lib/common/extension/string_extension.dart
1 /// 资源类扩展方法 1 /// 资源类扩展方法
2 extension AssetExtension on String { 2 extension AssetExtension on String {
3 static const String _assetImagePrefix = "assets/images/"; 3 static const String _assetImagePrefix = "assets/images/";
  4 + static const String _assetSoundPrefix = 'sounds/';
4 5
5 /// 图片url 6 /// 图片url
6 String get assetImg => _assetImagePrefix + this; 7 String get assetImg => _assetImagePrefix + this;
@@ -10,6 +11,10 @@ extension AssetExtension on String { @@ -10,6 +11,10 @@ extension AssetExtension on String {
10 String get assetWebp => '$assetImg.webp'; 11 String get assetWebp => '$assetImg.webp';
11 12
12 String get assetGif => '$assetImg.gif'; 13 String get assetGif => '$assetImg.gif';
  14 +
  15 + String get assetSound => _assetSoundPrefix + this;
  16 +
  17 + String get assetMp3 => '$assetSound.mp3';
13 } 18 }
14 19
15 extension StringExtension on String { 20 extension StringExtension on String {
lib/pages/practice/bloc/topic_picture_bloc.dart
@@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; @@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
5 import 'package:flutter_bloc/flutter_bloc.dart'; 5 import 'package:flutter_bloc/flutter_bloc.dart';
6 import 'package:flutter_easyloading/flutter_easyloading.dart'; 6 import 'package:flutter_easyloading/flutter_easyloading.dart';
7 import 'package:permission_handler/permission_handler.dart'; 7 import 'package:permission_handler/permission_handler.dart';
  8 +import 'package:wow_english/common/extension/string_extension.dart';
8 import 'package:wow_english/common/request/dao/listen_dao.dart'; 9 import 'package:wow_english/common/request/dao/listen_dao.dart';
9 import 'package:wow_english/common/request/exception.dart'; 10 import 'package:wow_english/common/request/exception.dart';
10 import 'package:wow_english/models/course_process_entity.dart'; 11 import 'package:wow_english/models/course_process_entity.dart';
@@ -45,6 +46,16 @@ class TopicPictureBloc extends Bloc<TopicPictureEvent, TopicPictureState> { @@ -45,6 +46,16 @@ class TopicPictureBloc extends Bloc<TopicPictureEvent, TopicPictureState> {
45 ///正在播放音频 46 ///正在播放音频
46 VoicePlayState _voicePlayState = VoicePlayState.unKnow; 47 VoicePlayState _voicePlayState = VoicePlayState.unKnow;
47 48
  49 + // 是否是回答(选择)结果音效
  50 + bool _isResultSoundPlaying = false;
  51 +
  52 + bool get isResultSoundPlaying => _isResultSoundPlaying;
  53 +
  54 + // 答对播放音效时禁止任何点击(选择)操作
  55 + bool _forbiddenWhenCorrect = false;
  56 +
  57 + bool get forbiddenWhenCorrect => _forbiddenWhenCorrect;
  58 +
48 CourseProcessEntity? get entity => _entity; 59 CourseProcessEntity? get entity => _entity;
49 60
50 int get currentPage => _currentPage + 1; 61 int get currentPage => _currentPage + 1;
@@ -70,29 +81,43 @@ class TopicPictureBloc extends Bloc<TopicPictureEvent, TopicPictureState> { @@ -70,29 +81,43 @@ class TopicPictureBloc extends Bloc<TopicPictureEvent, TopicPictureState> {
70 on<RequestDataEvent>(_requestData); 81 on<RequestDataEvent>(_requestData);
71 on<XSVoiceTestEvent>(_voiceXsTest); 82 on<XSVoiceTestEvent>(_voiceXsTest);
72 on<XSVoiceStopEvent>(_voiceXsStop); 83 on<XSVoiceStopEvent>(_voiceXsStop);
73 - on<VoicePlayEvent>(_voicePlay); 84 + on<VoicePlayEvent>(_questionVoicePlay);
74 on<InitBlocEvent>((event, emit) { 85 on<InitBlocEvent>((event, emit) {
75 //音频播放器 86 //音频播放器
76 audioPlayer = AudioPlayer(); 87 audioPlayer = AudioPlayer();
77 audioPlayer.onPlayerStateChanged.listen((event) async { 88 audioPlayer.onPlayerStateChanged.listen((event) async {
78 - debugPrint('播放状态变化');  
79 - if (event == PlayerState.completed) {  
80 - debugPrint('播放完成');  
81 - _voicePlayState = VoicePlayState.completed;  
82 - }  
83 - if (event == PlayerState.stopped) {  
84 - debugPrint('播放结束');  
85 - _voicePlayState = VoicePlayState.stop;  
86 - } 89 + debugPrint('播放状态变化 _voicePlayState=$_voicePlayState event=$event _isResultSoundPlaying=$_isResultSoundPlaying _forbiddenWhenCorrect=$_forbiddenWhenCorrect');
  90 + if (_isResultSoundPlaying) {
  91 + if (event != PlayerState.playing) {
  92 + _isResultSoundPlaying = false;
  93 + if (_forbiddenWhenCorrect) {
  94 + _forbiddenWhenCorrect = false;
  95 + // 答对后自动翻页
  96 + pageController.nextPage(
  97 + duration: const Duration(milliseconds: 500),
  98 + curve: Curves.ease,
  99 + );
  100 + }
  101 + }
  102 + } else {
  103 + if (event == PlayerState.completed) {
  104 + debugPrint('播放完成');
  105 + _voicePlayState = VoicePlayState.completed;
  106 + }
  107 + if (event == PlayerState.stopped) {
  108 + debugPrint('播放结束');
  109 + _voicePlayState = VoicePlayState.stop;
  110 + }
87 111
88 - if (event == PlayerState.playing) {  
89 - debugPrint('正在播放中');  
90 - _voicePlayState = VoicePlayState.playing;  
91 - }  
92 - if(isClosed) {  
93 - return; 112 + if (event == PlayerState.playing) {
  113 + debugPrint('正在播放中');
  114 + _voicePlayState = VoicePlayState.playing;
  115 + }
  116 + if (isClosed) {
  117 + return;
  118 + }
  119 + add(VoicePlayStateChangeEvent());
94 } 120 }
95 - add(VoicePlayStateChangeEvent());  
96 }); 121 });
97 122
98 methodChannel = const MethodChannel('wow_english/sing_sound_method_channel'); 123 methodChannel = const MethodChannel('wow_english/sing_sound_method_channel');
@@ -129,6 +154,8 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; { @@ -129,6 +154,8 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
129 pageController.dispose(); 154 pageController.dispose();
130 audioPlayer.release(); 155 audioPlayer.release();
131 audioPlayer.dispose(); 156 audioPlayer.dispose();
  157 + _isResultSoundPlaying = false;
  158 + _forbiddenWhenCorrect = false;
132 _voiceXsCancel(); 159 _voiceXsCancel();
133 return super.close(); 160 return super.close();
134 } 161 }
@@ -149,10 +176,12 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; { @@ -149,10 +176,12 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
149 176
150 ///页面切换 177 ///页面切换
151 void _pageControllerChange(CurrentPageIndexChangeEvent event,Emitter<TopicPictureState> emitter) async { 178 void _pageControllerChange(CurrentPageIndexChangeEvent event,Emitter<TopicPictureState> emitter) async {
152 - _currentPage = event.pageIndex;  
153 - if (voicePlayState == VoicePlayState.playing) {  
154 - await audioPlayer.stop(); 179 + await closePlayerResource();
  180 + debugPrint('翻页 $_currentPage->${event.pageIndex}');
  181 + if (_currentPage == _entity?.topics?.length) {
  182 + return;
155 } 183 }
  184 + _currentPage = event.pageIndex;
156 final topics = _entity?.topics?[_currentPage]; 185 final topics = _entity?.topics?[_currentPage];
157 if (topics?.type != 3 && topics?.type != 4) { 186 if (topics?.type != 3 && topics?.type != 4) {
158 if (topics?.audioUrl != null) { 187 if (topics?.audioUrl != null) {
@@ -169,13 +198,18 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; { @@ -169,13 +198,18 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
169 198
170 ///选择 199 ///选择
171 void _selectItemLoad(SelectItemEvent event,Emitter<TopicPictureState> emitter) async { 200 void _selectItemLoad(SelectItemEvent event,Emitter<TopicPictureState> emitter) async {
  201 + if (_forbiddenWhenCorrect) {
  202 + return;
  203 + }
172 _selectItem = event.selectIndex; 204 _selectItem = event.selectIndex;
173 CourseProcessTopics? topics = _entity?.topics?[_currentPage]; 205 CourseProcessTopics? topics = _entity?.topics?[_currentPage];
174 CourseProcessTopicsTopicAnswerList? answerList = topics?.topicAnswerList?[_selectItem]; 206 CourseProcessTopicsTopicAnswerList? answerList = topics?.topicAnswerList?[_selectItem];
175 if (answerList?.correct == 0) { 207 if (answerList?.correct == 0) {
176 - showToast('继续加油哦',duration: const Duration(seconds: 2)); 208 + _playResultSound(false);
  209 + // showToast('继续加油哦',duration: const Duration(seconds: 2));
177 } else { 210 } else {
178 - showToast('恭喜你,答对啦!',duration: const Duration(seconds: 2)); 211 + _playResultSound(true);
  212 + // showToast('恭喜你,答对啦!',duration: const Duration(seconds: 2));
179 } 213 }
180 emitter(SelectItemChangeState()); 214 emitter(SelectItemChangeState());
181 } 215 }
@@ -228,17 +262,37 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; { @@ -228,17 +262,37 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
228 emitter(XSVoiceTestState()); 262 emitter(XSVoiceTestState());
229 } 263 }
230 264
  265 + // 暂时没用上
231 void _voicePlayStateChange(VoicePlayStateChangeEvent event,Emitter<TopicPictureState> emitter) async { 266 void _voicePlayStateChange(VoicePlayStateChangeEvent event,Emitter<TopicPictureState> emitter) async {
232 emitter(VoicePlayStateChange()); 267 emitter(VoicePlayStateChange());
233 } 268 }
234 269
235 - void _voicePlay(VoicePlayEvent event,Emitter<TopicPictureState> emitter) async {  
236 - if (voicePlayState == VoicePlayState.playing) {  
237 - await audioPlayer.stop(); 270 + // 题目音频播放
  271 + void _questionVoicePlay(VoicePlayEvent event,Emitter<TopicPictureState> emitter) async {
  272 + if (_forbiddenWhenCorrect) {
238 return; 273 return;
239 } 274 }
  275 + _forbiddenWhenCorrect = false;
  276 + await closePlayerResource();
240 final topics = _entity?.topics?[_currentPage]; 277 final topics = _entity?.topics?[_currentPage];
241 final urlStr = topics?.audioUrl??''; 278 final urlStr = topics?.audioUrl??'';
242 await audioPlayer.play(UrlSource(urlStr)); 279 await audioPlayer.play(UrlSource(urlStr));
243 } 280 }
  281 +
  282 + Future<void> closePlayerResource() async {
  283 + if (voicePlayState == VoicePlayState.playing || _isResultSoundPlaying) {
  284 + await audioPlayer.stop();
  285 + }
  286 + }
  287 +
  288 + void _playResultSound(bool isCorrect) async {
  289 + await audioPlayer.stop();
  290 + _isResultSoundPlaying = true;
  291 + _forbiddenWhenCorrect = isCorrect;
  292 + if (isCorrect) {
  293 + await audioPlayer.play(AssetSource('correct_voice'.assetMp3));
  294 + } else {
  295 + await audioPlayer.play(AssetSource('incorrect_voice'.assetMp3));
  296 + }
  297 + }
244 } 298 }
lib/pages/practice/bloc/topic_picture_event.dart
@@ -48,5 +48,5 @@ class SelectItemEvent extends TopicPictureEvent { @@ -48,5 +48,5 @@ class SelectItemEvent extends TopicPictureEvent {
48 ///音频播放事件 48 ///音频播放事件
49 class VoicePlayChangeEvent extends TopicPictureEvent {} 49 class VoicePlayChangeEvent extends TopicPictureEvent {}
50 50
51 -///播放音乐 51 +///播放(题目)音乐
52 class VoicePlayEvent extends TopicPictureEvent {} 52 class VoicePlayEvent extends TopicPictureEvent {}
53 \ No newline at end of file 53 \ No newline at end of file
pubspec.yaml
@@ -94,15 +94,15 @@ dependencies: @@ -94,15 +94,15 @@ dependencies:
94 # UI适配 https://pub.dev/packages/responsive_framework 94 # UI适配 https://pub.dev/packages/responsive_framework
95 responsive_framework: ^1.0.0 95 responsive_framework: ^1.0.0
96 # 音频播放 https://pub.dev/packages/audioplayers 96 # 音频播放 https://pub.dev/packages/audioplayers
97 - audioplayers: ^4.1.0 97 + audioplayers: ^6.0.0
98 # 语音录制 https://pub.dev/packages/flutter_sound 98 # 语音录制 https://pub.dev/packages/flutter_sound
99 flutter_sound: ^9.2.13 99 flutter_sound: ^9.2.13
100 # 音频播放 https://pub.dev/packages/audio_session 100 # 音频播放 https://pub.dev/packages/audio_session
101 - audio_session: ^0.1.16 101 + audio_session: ^0.1.19
102 # 文件管理 https://pub.dev/packages/path_provider 102 # 文件管理 https://pub.dev/packages/path_provider
103 path_provider: ^2.0.15 103 path_provider: ^2.0.15
104 # 阿里云oss https://pub.dev/packages/flutter_oss_aliyun 104 # 阿里云oss https://pub.dev/packages/flutter_oss_aliyun
105 - flutter_oss_aliyun: ^6.2.7 105 + flutter_oss_aliyun: ^6.4.2
106 # App信息 https://pub.dev/packages/package_info_plus 106 # App信息 https://pub.dev/packages/package_info_plus
107 package_info_plus: ^4.2.0 107 package_info_plus: ^4.2.0
108 # 应用内更新 https://pub-web.flutter-io.cn/packages/flutter_app_update 108 # 应用内更新 https://pub-web.flutter-io.cn/packages/flutter_app_update
@@ -135,6 +135,7 @@ flutter: @@ -135,6 +135,7 @@ flutter:
135 assets: 135 assets:
136 - assets/images/ 136 - assets/images/
137 - assets/fonts/ 137 - assets/fonts/
  138 + - assets/sounds/
138 fonts: 139 fonts:
139 - family: HannotateSC 140 - family: HannotateSC
140 fonts: 141 fonts: