Commit 065022b77e5d916f521551d108d7eadd9a839516
1 parent
cc3bbe3d
feat:绘本评测反馈弹窗+原音播放&录音播放&开始录音互斥逻辑
Showing
13 changed files
with
242 additions
and
46 deletions
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/methodChannels/SingSoungMethodChannel.kt
| ... | ... | @@ -61,10 +61,16 @@ class SingSoungMethodChannel(activity: FlutterActivity, flutterEngine: FlutterEn |
| 61 | 61 | SingEngineHelper.addOnResultListener(this) |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | - override fun onResult(jsonObject: JSONObject, evalType: Int?) { | |
| 64 | + override fun onResult(map: Map<String, Any>, evalType: Int?) { | |
| 65 | 65 | //先声回调在子线程,需要切换到主线程 |
| 66 | 66 | GlobalHandler.runOnMainThread { |
| 67 | - invokeMethod("voiceResult", jsonObject.toString()) | |
| 67 | + invokeMethod("voiceResult", map) | |
| 68 | + } | |
| 69 | + } | |
| 70 | + | |
| 71 | + override fun onRecordFail(code: Int, message: String) { | |
| 72 | + GlobalHandler.runOnMainThread { | |
| 73 | + invokeMethod("voiceFail", mapOf("code" to code, "message" to message)) | |
| 68 | 74 | } |
| 69 | 75 | } |
| 70 | 76 | ... | ... |
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/singsound/OnSingEngineLifecycles.kt
| ... | ... | @@ -14,11 +14,18 @@ interface SingEngineLifecycles { |
| 14 | 14 | fun onRecordPlayOver() |
| 15 | 15 | |
| 16 | 16 | // 评测完成 |
| 17 | - fun onResult(jsonObject: JSONObject, @EvalTargetType evalType: Int? = EvalTargetType.SENTENCE) | |
| 17 | + fun onResult(map: Map<String, Any>, @EvalTargetType evalType: Int? = EvalTargetType.SENTENCE) | |
| 18 | 18 | |
| 19 | 19 | // 取消评测 |
| 20 | 20 | fun onCancel() |
| 21 | 21 | |
| 22 | + /** | |
| 23 | + * 评测失败 | |
| 24 | + * @param code 失败错误码 | |
| 25 | + * @param message 失败错误信息 | |
| 26 | + */ | |
| 27 | + fun onRecordFail(code: Int, message: String) | |
| 28 | + | |
| 22 | 29 | |
| 23 | 30 | abstract class OnSingEngineAdapter : SingEngineLifecycles { |
| 24 | 31 | override fun onRecordBegin() { |
| ... | ... | @@ -33,12 +40,16 @@ interface SingEngineLifecycles { |
| 33 | 40 | |
| 34 | 41 | } |
| 35 | 42 | |
| 36 | - override fun onResult(jsonObject: JSONObject, @EvalTargetType evalType: Int?) { | |
| 43 | + override fun onResult(map: Map<String, Any>, @EvalTargetType evalType: Int?) { | |
| 37 | 44 | |
| 38 | 45 | } |
| 39 | 46 | |
| 40 | 47 | override fun onCancel() { |
| 41 | 48 | |
| 42 | 49 | } |
| 50 | + | |
| 51 | + override fun onRecordFail(code: Int, message: String) { | |
| 52 | + | |
| 53 | + } | |
| 43 | 54 | } |
| 44 | 55 | } |
| 45 | 56 | \ No newline at end of file | ... | ... |
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/singsound/SingEngineHelper.kt
| ... | ... | @@ -9,6 +9,7 @@ import com.kouyuxingqiu.wow_english.singsound.config.EvalTargetType |
| 9 | 9 | import com.kouyuxingqiu.wow_english.singsound.config.SingSoundConfig |
| 10 | 10 | import com.kouyuxingqiu.wow_english.singsound.config.VoiceConfig |
| 11 | 11 | import com.kouyuxingqiu.wow_english.singsound.config.WordConfig |
| 12 | +import com.kouyuxingqiu.wow_english.singsound.util.JsonUtils.toMap | |
| 12 | 13 | import com.xs.SingEngine |
| 13 | 14 | import com.xs.impl.AudioErrorCallback |
| 14 | 15 | import com.xs.impl.EvalReturnRequestIdCallback |
| ... | ... | @@ -279,11 +280,10 @@ object SingEngineHelper : |
| 279 | 280 | */ |
| 280 | 281 | override fun onResult(jsonObject: JSONObject) { |
| 281 | 282 | Log.i(TAG, "onResult = $jsonObject") |
| 282 | - parseResult(jsonObject) | |
| 283 | 283 | setTokenToCache(jsonObject) |
| 284 | 284 | mListeners?.let { |
| 285 | 285 | for (callback in it) { |
| 286 | - callback.onResult(jsonObject, mCurEvalType) | |
| 286 | + callback.onResult(toMap(jsonObject), mCurEvalType) | |
| 287 | 287 | } |
| 288 | 288 | } |
| 289 | 289 | } |
| ... | ... | @@ -376,6 +376,13 @@ object SingEngineHelper : |
| 376 | 376 | */ |
| 377 | 377 | override fun onEnd(resultBody: ResultBody) { |
| 378 | 378 | Log.i(TAG, "onEnd resultBody=$resultBody") |
| 379 | + if (resultBody.code != 0) { | |
| 380 | + mListeners?.let { | |
| 381 | + for (callback in it) { | |
| 382 | + callback.onRecordFail(resultBody.code, resultBody.message) | |
| 383 | + } | |
| 384 | + } | |
| 385 | + } | |
| 379 | 386 | } |
| 380 | 387 | |
| 381 | 388 | override fun onGetEvalRequestId(p0: String?) { | ... | ... |
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/singsound/util/JsonUtils.java
| ... | ... | @@ -2,10 +2,21 @@ package com.kouyuxingqiu.wow_english.singsound.util; |
| 2 | 2 | |
| 3 | 3 | import android.util.Log; |
| 4 | 4 | |
| 5 | +import com.google.gson.Gson; | |
| 6 | +import com.google.gson.JsonElement; | |
| 7 | +import com.google.gson.JsonObject; | |
| 8 | + | |
| 5 | 9 | import org.json.JSONArray; |
| 6 | 10 | import org.json.JSONException; |
| 7 | 11 | import org.json.JSONObject; |
| 8 | 12 | |
| 13 | +import java.util.ArrayList; | |
| 14 | +import java.util.HashMap; | |
| 15 | +import java.util.Iterator; | |
| 16 | +import java.util.List; | |
| 17 | +import java.util.Map; | |
| 18 | +import java.util.Set; | |
| 19 | + | |
| 9 | 20 | /** |
| 10 | 21 | * Created by wangz on 2017/8/29. |
| 11 | 22 | */ |
| ... | ... | @@ -84,4 +95,42 @@ public class JsonUtils { |
| 84 | 95 | return false; |
| 85 | 96 | } |
| 86 | 97 | } |
| 98 | + | |
| 99 | + public static Map<String, Object> toMap(JSONObject jsonObject) throws JSONException { | |
| 100 | + Map<String, Object> map = new HashMap<>(); | |
| 101 | + Iterator<String> keysIterator = jsonObject.keys(); | |
| 102 | + while (keysIterator.hasNext()) { | |
| 103 | + String key = keysIterator.next(); | |
| 104 | + Object value = jsonObject.get(key); | |
| 105 | + if (value instanceof JSONObject) { | |
| 106 | + value = toMap((JSONObject) value); | |
| 107 | + } | |
| 108 | + if (value instanceof JSONArray) { | |
| 109 | + value = toList((JSONArray) value); | |
| 110 | + } | |
| 111 | + map.put(key, value); | |
| 112 | + } | |
| 113 | + return map; | |
| 114 | + } | |
| 115 | + | |
| 116 | + public static List<Object> toList(JSONArray jsonArray) throws JSONException { | |
| 117 | + List<Object> list = new ArrayList<>(); | |
| 118 | + for (int i = 0; i < jsonArray.length(); i++) { | |
| 119 | + Object value = jsonArray.get(i); | |
| 120 | + if (value instanceof JSONObject || value instanceof JSONArray) { | |
| 121 | + value = toObject(value); | |
| 122 | + } | |
| 123 | + list.add(value); | |
| 124 | + } | |
| 125 | + return list; | |
| 126 | + } | |
| 127 | + | |
| 128 | + public static Object toObject(Object json) throws JSONException { | |
| 129 | + if (json instanceof JSONObject) { | |
| 130 | + return toMap((JSONObject) json); | |
| 131 | + } else if (json instanceof JSONArray) { | |
| 132 | + return toList((JSONArray) json); | |
| 133 | + } | |
| 134 | + return json; | |
| 135 | + } | |
| 87 | 136 | } | ... | ... |
assets/images/pic_very_good.webp
0 → 100644
No preview for this file type
assets/images/record_pause.webp
No preview for this file type
assets/images/record_play.webp
No preview for this file type
assets/images/text_very_good.webp
0 → 100644
No preview for this file type
lib/pages/reading/bloc/reading_bloc.dart
| ... | ... | @@ -12,6 +12,8 @@ import '../../../models/course_process_entity.dart'; |
| 12 | 12 | import '../../../utils/loading.dart'; |
| 13 | 13 | import 'dart:convert'; |
| 14 | 14 | |
| 15 | +import '../../../utils/log_util.dart'; | |
| 16 | + | |
| 15 | 17 | part 'reading_event.dart'; |
| 16 | 18 | part 'reading_state.dart'; |
| 17 | 19 | |
| ... | ... | @@ -38,7 +40,7 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 38 | 40 | int _currentPage = 0; |
| 39 | 41 | |
| 40 | 42 | ///当前播放模式 |
| 41 | - ReadingModeType _currentMode = ReadingModeType.auto; | |
| 43 | + ReadingModeType _currentMode = ReadingModeType.manual; | |
| 42 | 44 | |
| 43 | 45 | int get currentPage => _currentPage + 1; |
| 44 | 46 | |
| ... | ... | @@ -88,10 +90,12 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 88 | 90 | if (event == PlayerState.completed) { |
| 89 | 91 | debugPrint('播放完成'); |
| 90 | 92 | _voicePlayState = VoicePlayState.completed; |
| 93 | + _onAudioPlayComplete(); | |
| 91 | 94 | } |
| 92 | 95 | if (event == PlayerState.stopped) { |
| 93 | 96 | debugPrint('播放结束'); |
| 94 | 97 | _voicePlayState = VoicePlayState.stop; |
| 98 | + _onAudioPlayComplete(); | |
| 95 | 99 | } |
| 96 | 100 | |
| 97 | 101 | if (event == PlayerState.playing) { |
| ... | ... | @@ -108,6 +112,7 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 108 | 112 | const MethodChannel('wow_english/sing_sound_method_channel'); |
| 109 | 113 | methodChannel.invokeMethod('initVoiceSdk', {}); //初始化评测 |
| 110 | 114 | methodChannel.setMethodCallHandler((call) async { |
| 115 | + Log.d("setMethodCallHandler method=${call.method} arguments=${call.arguments}"); | |
| 111 | 116 | if (call.method == 'voiceResult') { |
| 112 | 117 | //评测结果 |
| 113 | 118 | add(XSVoiceResultEvent(call.arguments)); |
| ... | ... | @@ -119,6 +124,8 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 119 | 124 | if (kDebugMode) { |
| 120 | 125 | print('评测开始'); |
| 121 | 126 | } |
| 127 | + _isRecording = true; | |
| 128 | + add(OnXSVoiceStateChangeEvent()); | |
| 122 | 129 | return; |
| 123 | 130 | } |
| 124 | 131 | |
| ... | ... | @@ -127,6 +134,8 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 127 | 134 | if (kDebugMode) { |
| 128 | 135 | print('评测结束'); |
| 129 | 136 | } |
| 137 | + _isRecording = false; | |
| 138 | + add(OnXSVoiceStateChangeEvent()); | |
| 130 | 139 | return; |
| 131 | 140 | } |
| 132 | 141 | |
| ... | ... | @@ -143,6 +152,7 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 143 | 152 | on<XSVoiceStartEvent>(_voiceXsStart); |
| 144 | 153 | on<XSVoiceStopEvent>(_voiceXsStop); |
| 145 | 154 | on<XSVoiceResultEvent>(_voiceXsResult); |
| 155 | + on<OnXSVoiceStateChangeEvent>(_onVoiceXsStateChange); | |
| 146 | 156 | |
| 147 | 157 | on<PlayRecordAudioEvent>(_playRecordAudio); |
| 148 | 158 | } |
| ... | ... | @@ -178,7 +188,7 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 178 | 188 | try { |
| 179 | 189 | await loading(() async { |
| 180 | 190 | _entity = await ListenDao.process(courseLessonId); |
| 181 | - print("reading page entity: ${_entity!.toJson()}"); | |
| 191 | + Log.d("reading page entity: ${_entity!.toJson()}"); | |
| 182 | 192 | emitter(RequestDataState()); |
| 183 | 193 | }); |
| 184 | 194 | } catch (e) { |
| ... | ... | @@ -194,11 +204,20 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 194 | 204 | _playOriginalAudioInner(event.url); |
| 195 | 205 | } |
| 196 | 206 | |
| 197 | - void _playOriginalAudioInner(String? audioUrl) { | |
| 198 | - print("_playOriginalAudio url=$audioUrl"); | |
| 199 | - audioUrl ??= currentPageData()?.audioUrl ?? ''; | |
| 200 | - _playAudio(audioUrl); | |
| 201 | - _isOriginAudioPlaying = true; | |
| 207 | + ///播放原音音频 | |
| 208 | + Future<void> _playOriginalAudioInner(String? audioUrl) async { | |
| 209 | + if (_isRecordAudioPlaying) { | |
| 210 | + _isRecordAudioPlaying = false; | |
| 211 | + } | |
| 212 | + Log.d("_playOriginalAudio _isRecordAudioPlaying=$_isRecordAudioPlaying _isOriginAudioPlaying=$_isOriginAudioPlaying url=$audioUrl"); | |
| 213 | + if (_isOriginAudioPlaying) { | |
| 214 | + _isOriginAudioPlaying = false; | |
| 215 | + await audioPlayer.stop(); | |
| 216 | + } else { | |
| 217 | + _isOriginAudioPlaying = true; | |
| 218 | + audioUrl ??= currentPageData()?.audioUrl ?? ''; | |
| 219 | + _playAudio(audioUrl); | |
| 220 | + } | |
| 202 | 221 | } |
| 203 | 222 | |
| 204 | 223 | /// 播放录音 |
| ... | ... | @@ -207,22 +226,29 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 207 | 226 | _playRecordAudioInner(); |
| 208 | 227 | } |
| 209 | 228 | |
| 210 | - void _playRecordAudioInner() { | |
| 211 | - final recordAudioUrl = currentPageData()?.recordUrl; | |
| 212 | - print("_playRecordAudioInner url=${currentPageData()?.recordUrl}"); | |
| 213 | - _playAudio(recordAudioUrl); | |
| 214 | - _isRecordAudioPlaying = true; | |
| 229 | + Future<void> _playRecordAudioInner() async { | |
| 230 | + if (_isOriginAudioPlaying) { | |
| 231 | + _isOriginAudioPlaying = false; | |
| 232 | + } | |
| 233 | + Log.d("_playRecordAudioInner _isRecordAudioPlaying=$_isRecordAudioPlaying url=${currentPageData()?.recordUrl}"); | |
| 234 | + if (_isRecordAudioPlaying) { | |
| 235 | + _isRecordAudioPlaying = false; | |
| 236 | + await audioPlayer.stop(); | |
| 237 | + } else { | |
| 238 | + _isRecordAudioPlaying = true; | |
| 239 | + final recordAudioUrl = currentPageData()?.recordUrl; | |
| 240 | + _playAudio(recordAudioUrl); | |
| 241 | + } | |
| 242 | + // emit(VoicePlayStateChange()); | |
| 215 | 243 | } |
| 216 | 244 | |
| 217 | 245 | void _playAudio(String? audioUrl) async { |
| 218 | - await audioPlayer.stop(); | |
| 219 | 246 | if (audioUrl!.isNotEmpty) { |
| 220 | 247 | await audioPlayer.play(UrlSource(audioUrl)); |
| 221 | 248 | } |
| 222 | 249 | } |
| 223 | 250 | |
| 224 | 251 | int dataCount() { |
| 225 | - // print("dataCount=${_entity?.readings?.length ?? 0}"); | |
| 226 | 252 | return _entity?.readings?.length ?? 0; |
| 227 | 253 | } |
| 228 | 254 | |
| ... | ... | @@ -230,6 +256,18 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 230 | 256 | return _entity?.readings?[_currentPage]; |
| 231 | 257 | } |
| 232 | 258 | |
| 259 | + void nextPage() { | |
| 260 | + if (_currentPage >= dataCount() - 1) { | |
| 261 | + ///todo 最后一页了 | |
| 262 | + } else { | |
| 263 | + _currentPage += 1; | |
| 264 | + pageController.nextPage( | |
| 265 | + duration: const Duration(milliseconds: 500), | |
| 266 | + curve: Curves.ease, | |
| 267 | + ); | |
| 268 | + } | |
| 269 | + } | |
| 270 | + | |
| 233 | 271 | ///初始化SDK |
| 234 | 272 | _initVoiceSdk( |
| 235 | 273 | XSVoiceInitEvent event, Emitter<ReadingPageState> emitter) async { |
| ... | ... | @@ -241,7 +279,6 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 241 | 279 | XSVoiceStartEvent event, Emitter<ReadingPageState> emitter) async { |
| 242 | 280 | _stopAudio(); |
| 243 | 281 | startRecord(event.content); |
| 244 | - emitter(XSVoiceTestState()); | |
| 245 | 282 | } |
| 246 | 283 | |
| 247 | 284 | void startRecord(String content) async { |
| ... | ... | @@ -249,27 +286,22 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 249 | 286 | return; |
| 250 | 287 | } |
| 251 | 288 | methodChannel.invokeMethod( |
| 252 | - 'startVoice', {'word': 'how old are you', 'type': '0', 'userId': '1'}); | |
| 253 | - _isRecording = true; | |
| 289 | + 'startVoice', {'word': content, 'type': '0', 'userId': '1'}); | |
| 254 | 290 | } |
| 255 | 291 | |
| 256 | 292 | void _voiceXsResult( |
| 257 | 293 | XSVoiceResultEvent event, Emitter<ReadingPageState> emitter) async { |
| 258 | - final Map args = json.decode(event.message); | |
| 259 | - final result = args['result']; | |
| 260 | - print("_voiceXsResult result=${result}"); | |
| 261 | - if (result != null) { | |
| 262 | - final overall = result['overall'].toString(); | |
| 263 | - EasyLoading.showToast('测评成功,分数是$overall', | |
| 264 | - duration: const Duration(seconds: 10)); | |
| 265 | - currentPageData()?.recordScore = overall; | |
| 266 | - currentPageData()?.recordUrl = args['audioUrl'] + '.mp3'; | |
| 267 | - _playRecordAudioInner(); | |
| 268 | - } else { | |
| 269 | - EasyLoading.showToast('测评失败', duration: const Duration(seconds: 10)); | |
| 270 | - } | |
| 271 | - _isRecording = false; | |
| 272 | - emitter(XSVoiceTestState()); | |
| 294 | + final Map args = event.message as Map; | |
| 295 | + final result = args['result'] as Map; | |
| 296 | + Log.d("_voiceXsResult result=$result"); | |
| 297 | + final overall = result['overall'].toString(); | |
| 298 | + EasyLoading.showToast('测评成功,分数是$overall', | |
| 299 | + duration: const Duration(seconds: 10)); | |
| 300 | + currentPageData()?.recordScore = overall; | |
| 301 | + currentPageData()?.recordUrl = args['audioUrl'] + '.mp3'; | |
| 302 | + ///完成录音后紧接着播放录音 | |
| 303 | + _playRecordAudioInner(); | |
| 304 | + emitter(FeedbackState()); | |
| 273 | 305 | } |
| 274 | 306 | |
| 275 | 307 | ///终止评测 |
| ... | ... | @@ -283,9 +315,32 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 283 | 315 | emitter(VoicePlayStateChange()); |
| 284 | 316 | } |
| 285 | 317 | |
| 318 | + void _onAudioPlayComplete() { | |
| 319 | + if (_isRecordAudioPlaying && _currentMode == ReadingModeType.auto) { | |
| 320 | + nextPage(); | |
| 321 | + } | |
| 322 | + | |
| 323 | + Log.d("_onAudioPlayComplete _isOriginAudioPlaying=${_isOriginAudioPlaying} _voicePlayState=$_voicePlayState recordUrl=${currentPageData()?.recordUrl?.isNotEmpty}"); | |
| 324 | + if (_isOriginAudioPlaying && _voicePlayState == VoicePlayState.completed && currentPageData()?.recordUrl?.isNotEmpty != true) { | |
| 325 | + ///如果刚刚完成原音播放&&录音为空,则开始录音 | |
| 326 | + startRecord(currentPageData()?.word ?? ''); | |
| 327 | + } | |
| 328 | + | |
| 329 | + _isOriginAudioPlaying = false; | |
| 330 | + _isRecordAudioPlaying = false; | |
| 331 | + } | |
| 332 | + | |
| 286 | 333 | void _stopAudio() async { |
| 287 | 334 | await audioPlayer.stop(); |
| 288 | 335 | _isOriginAudioPlaying = false; |
| 289 | 336 | _isRecordAudioPlaying = false; |
| 290 | 337 | } |
| 338 | + | |
| 339 | + void _onVoiceXsStateChange( | |
| 340 | + OnXSVoiceStateChangeEvent event, | |
| 341 | + Emitter<ReadingPageState> emitter | |
| 342 | + ) async { | |
| 343 | + emit(XSVoiceTestState()); | |
| 344 | + } | |
| 291 | 345 | } |
| 346 | + | ... | ... |
lib/pages/reading/bloc/reading_event.dart
| ... | ... | @@ -45,6 +45,9 @@ class XSVoiceStartEvent extends ReadingPageEvent { |
| 45 | 45 | ///先声评测停止 |
| 46 | 46 | class XSVoiceStopEvent extends ReadingPageEvent {} |
| 47 | 47 | |
| 48 | +///先声评测状态 | |
| 49 | +class OnXSVoiceStateChangeEvent extends ReadingPageEvent {} | |
| 50 | + | |
| 48 | 51 | ///音频播放状态 |
| 49 | 52 | class VoicePlayStateChangeEvent extends ReadingPageEvent {} |
| 50 | 53 | ... | ... |
lib/pages/reading/bloc/reading_state.dart
lib/pages/reading/reading_page.dart
| ... | ... | @@ -3,9 +3,11 @@ import 'package:flutter_bloc/flutter_bloc.dart'; |
| 3 | 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; |
| 4 | 4 | import 'package:wow_english/common/extension/string_extension.dart'; |
| 5 | 5 | import 'package:wow_english/pages/reading/widgets/ReadingModeType.dart'; |
| 6 | +import 'package:wow_english/pages/reading/widgets/reading_dialog_widget.dart'; | |
| 6 | 7 | |
| 7 | 8 | import '../../common/core/user_util.dart'; |
| 8 | 9 | import '../../models/course_process_entity.dart'; |
| 10 | +import '../../utils/log_util.dart'; | |
| 9 | 11 | import 'bloc/reading_bloc.dart'; |
| 10 | 12 | |
| 11 | 13 | class ReadingPage extends StatelessWidget { |
| ... | ... | @@ -29,21 +31,26 @@ class _ReadingPage extends StatelessWidget { |
| 29 | 31 | Widget build(BuildContext context) { |
| 30 | 32 | return BlocListener<ReadingPageBloc, ReadingPageState>( |
| 31 | 33 | listener: (context, state) { |
| 34 | + Log.d('reading BlocListener=$state'); | |
| 32 | 35 | if (state is RequestDataState) { |
| 33 | - // context.read<TopicPictureBloc>().add(CurrentPageIndexChangeEvent(0)); | |
| 34 | - print('reading RequestDataState=$state'); | |
| 35 | - | |
| 36 | 36 | ///刷新页面 |
| 37 | 37 | context.read<ReadingPageBloc>().add(CurrentPageIndexChangeEvent(0)); |
| 38 | 38 | } |
| 39 | + if (state is FeedbackState) { | |
| 40 | + showDialog<ReadingDialog>( | |
| 41 | + context: context, | |
| 42 | + barrierDismissible: false, | |
| 43 | + builder: (context) { | |
| 44 | + return const ReadingDialog(); | |
| 45 | + }); | |
| 46 | + } | |
| 39 | 47 | }, |
| 40 | 48 | child: _readingPageView(), |
| 41 | 49 | ); |
| 42 | 50 | } |
| 43 | 51 | |
| 44 | 52 | Widget _readingPageView() => BlocBuilder<ReadingPageBloc, ReadingPageState>( |
| 45 | - buildWhen: (_, s) => s is CurrentPageIndexState, | |
| 46 | - builder: (context, state) { | |
| 53 | + builder: (context, state) { | |
| 47 | 54 | final bloc = BlocProvider.of<ReadingPageBloc>(context); |
| 48 | 55 | return Container( |
| 49 | 56 | color: Colors.white, |
| ... | ... | @@ -176,7 +183,9 @@ class _ReadingPage extends StatelessWidget { |
| 176 | 183 | } |
| 177 | 184 | }, |
| 178 | 185 | child: Image.asset( |
| 179 | - 'micro_phone'.assetPng, | |
| 186 | + bloc.isRecording | |
| 187 | + ? 'micro_phone'.assetGif | |
| 188 | + : 'micro_phone'.assetPng, | |
| 180 | 189 | height: 47.h, |
| 181 | 190 | width: 47.w, |
| 182 | 191 | )), |
| ... | ... | @@ -213,8 +222,10 @@ class _ReadingPage extends StatelessWidget { |
| 213 | 222 | BlocBuilder<ReadingPageBloc, ReadingPageState>(builder: (context, state) { |
| 214 | 223 | return Stack( |
| 215 | 224 | children: [ |
| 216 | - Image.network(readings.picUrl ?? '', | |
| 217 | - height: double.infinity, width: double.infinity), | |
| 225 | + Positioned.fill( | |
| 226 | + child: | |
| 227 | + Image.network(readings.picUrl ?? '', fit: BoxFit.cover), | |
| 228 | + ), | |
| 218 | 229 | ], |
| 219 | 230 | ); |
| 220 | 231 | }); | ... | ... |
lib/pages/reading/widgets/reading_dialog_widget.dart
0 → 100644
| 1 | +import 'dart:async'; | |
| 2 | + | |
| 3 | +import 'package:flutter/material.dart'; | |
| 4 | +import 'package:flutter_screenutil/flutter_screenutil.dart'; | |
| 5 | +import 'package:wow_english/common/extension/string_extension.dart'; | |
| 6 | + | |
| 7 | +///评测结束反馈弹窗 | |
| 8 | +class ReadingDialog extends Dialog { | |
| 9 | + | |
| 10 | + const ReadingDialog({super.key}); | |
| 11 | + | |
| 12 | + //定时器,自动关闭Diolog | |
| 13 | + _showTimer(context) { | |
| 14 | + Timer.periodic(const Duration(milliseconds: 2000), //2000毫秒就是三秒 | |
| 15 | + (t) { | |
| 16 | + Navigator.pop(context); | |
| 17 | + t.cancel(); //取消定时器 timer.cancel(); | |
| 18 | + }); | |
| 19 | + } | |
| 20 | + | |
| 21 | + @override | |
| 22 | + Widget build(BuildContext context) { | |
| 23 | + _showTimer(context); | |
| 24 | + return Material( | |
| 25 | + type: MaterialType.transparency, | |
| 26 | + child: Center( | |
| 27 | + child: Container( | |
| 28 | + width: 250, | |
| 29 | + height: double.infinity, | |
| 30 | + color: Colors.transparent, | |
| 31 | + child: Column( | |
| 32 | + crossAxisAlignment: CrossAxisAlignment.center, | |
| 33 | + mainAxisAlignment: MainAxisAlignment.center, | |
| 34 | + children: [ | |
| 35 | + Image.asset( | |
| 36 | + 'text_very_good'.assetWebp, | |
| 37 | + width: 237.w, | |
| 38 | + height: 42.h, | |
| 39 | + ), | |
| 40 | + Image.asset( | |
| 41 | + 'pic_very_good'.assetWebp, | |
| 42 | + width: 210.w, | |
| 43 | + height: 228.h, | |
| 44 | + ), | |
| 45 | + ], | |
| 46 | + ), | |
| 47 | + ), | |
| 48 | + ), | |
| 49 | + ); | |
| 50 | + } | |
| 51 | +} | ... | ... |