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 | +} | ... | ... |