Commit a506beff7f4ef20d2016b98e901c8aeda51af822
1 parent
608c05b4
feat:先声sdk方法找不到问题修复;绘本接口&逻辑
Showing
10 changed files
with
269 additions
and
81 deletions
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/MainActivity.kt
... | ... | @@ -16,6 +16,8 @@ class MainActivity : FlutterActivity() { |
16 | 16 | override fun onCreate(savedInstanceState: Bundle?) { |
17 | 17 | super.onCreate(savedInstanceState) |
18 | 18 | Log.i("WowEnglish", "MainActivity onCreate") |
19 | + | |
20 | + flutterEngine?.let { SingSoungMethodChannel(this, it) } | |
19 | 21 | } |
20 | 22 | |
21 | 23 | override fun onResume() { |
... | ... | @@ -46,9 +48,4 @@ class MainActivity : FlutterActivity() { |
46 | 48 | // 打开沉浸式 |
47 | 49 | WindowCompat.setDecorFitsSystemWindows(window, false)*/ |
48 | 50 | } |
49 | - | |
50 | - override fun configureFlutterEngine(flutterEngine: FlutterEngine) { | |
51 | - super.configureFlutterEngine(flutterEngine) | |
52 | - SingSoungMethodChannel(this, flutterEngine) | |
53 | - } | |
54 | 51 | } | ... | ... |
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/methodChannels/SingSoungMethodChannel.kt
1 | 1 | package com.kouyuxingqiu.wow_english.methodChannels |
2 | 2 | |
3 | +import android.util.Log | |
4 | +import com.kouyuxingqiu.wow_english.singsound.SingEngineHelper | |
3 | 5 | import io.flutter.embedding.android.FlutterActivity |
4 | 6 | import io.flutter.embedding.engine.FlutterEngine |
5 | 7 | import io.flutter.plugin.common.MethodChannel |
... | ... | @@ -30,10 +32,18 @@ class SingSoungMethodChannel(val activity: FlutterActivity, val flutterEngine: F |
30 | 32 | ) |
31 | 33 | methodChannel?.setMethodCallHandler { call, result -> |
32 | 34 | when (call.method) { |
33 | - "startRecord" -> { | |
34 | - val jsonStr = call.arguments as? String ?: return@setMethodCallHandler | |
35 | + "initVoiceSdk" -> { | |
36 | + SingEngineHelper.init(activity) | |
37 | + } | |
38 | + "startVoice" -> { | |
39 | + val paramMap = call.arguments as HashMap<String, String> | |
40 | + Log.d("WQF", "SingSoungMethodChannel startVoice=${call.arguments.javaClass} paramMap=$paramMap") | |
41 | + paramMap["word"]?.let { SingEngineHelper.startRecord(it) } | |
35 | 42 | //do nothing |
36 | 43 | } |
44 | + "stopVoice" -> { | |
45 | + Log.d("WQF", "SingSoungMethodChannel stopVoice") | |
46 | + } | |
37 | 47 | else -> { |
38 | 48 | result.notImplemented() |
39 | 49 | } | ... | ... |
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/singsound/SingEngineHelper.kt
... | ... | @@ -18,7 +18,7 @@ import org.json.JSONObject |
18 | 18 | import java.util.* |
19 | 19 | |
20 | 20 | |
21 | -class SingEngineHelper private constructor() : | |
21 | +object SingEngineHelper : | |
22 | 22 | AudioErrorCallback, EvalReturnRequestIdCallback, OnRealTimeResultListener { |
23 | 23 | |
24 | 24 | private val TAG = "SingEngineManager" |
... | ... | @@ -54,57 +54,59 @@ class SingEngineHelper private constructor() : |
54 | 54 | mListeners = mutableListOf() |
55 | 55 | if (mSingEngine == null) { |
56 | 56 | mSingEngine = SingEngine.newInstance(context) |
57 | - } | |
58 | - Thread { | |
59 | - try { | |
60 | - mSingEngine?.run { | |
61 | - // 设置测评结果监听器 | |
62 | - setListener(this@SingEngineHelper) | |
63 | - // 设置录音器初始化错误的回调 | |
64 | - setAudioErrorCallback(this@SingEngineHelper) | |
65 | - setEvalReturnRequestIdCallback(this@SingEngineHelper) | |
57 | + Thread { | |
58 | + try { | |
59 | + mSingEngine?.run { | |
60 | + // 设置测评结果监听器 | |
61 | + setListener(this@SingEngineHelper) | |
62 | + // 设置录音器初始化错误的回调 | |
63 | + setAudioErrorCallback(this@SingEngineHelper) | |
64 | + setEvalReturnRequestIdCallback(this@SingEngineHelper) | |
66 | 65 | // // 设置音频格式 |
67 | 66 | // setAudioType(AudioTypeEnum.WAV) |
68 | - // 设置引擎类型。引擎类型(在线CLOUD、 离线NATIVE、混合AUTO),默认使用在线引擎。 | |
69 | - setServerType(CoreProvideTypeEnum.CLOUD) | |
70 | - // 设置log日志级别 | |
71 | - setLogLevel(4) | |
72 | - // 禁用实时音量返回 | |
73 | - disableVolume() | |
74 | - // 设置录音音频路径 | |
75 | - wavPath = AiUtil.getFilesDir(context).path + "/userdata/sound_record/" | |
76 | - // 设置是否开启 VAD 功能 | |
77 | - setOpenVad(true, "vad.0.1.bin") | |
78 | - //setOpenVad(false, null); | |
79 | - // 设置 VAD 前置超时时间 | |
80 | - setFrontVadTime(3000) | |
67 | + // 设置引擎类型。引擎类型(在线CLOUD、 离线NATIVE、混合AUTO),默认使用在线引擎。 | |
68 | + setServerType(CoreProvideTypeEnum.CLOUD) | |
69 | + // 设置log日志级别 | |
70 | + setLogLevel(4) | |
71 | + // 禁用实时音量返回 | |
72 | + disableVolume() | |
73 | + // 设置录音音频路径 | |
74 | + wavPath = AiUtil.getFilesDir(context).path + "/userdata/sound_record/" | |
75 | + // 设置是否开启 VAD 功能 | |
76 | + setOpenVad(true, "vad.0.1.bin") | |
77 | + //setOpenVad(false, null); | |
78 | + // 设置 VAD 前置超时时间 | |
79 | + setFrontVadTime(3000) | |
81 | 80 | // setServerTimeout(10000) |
82 | - // 开启错误日志保存到本地,发生错误时文件中会保存到android/data/包名/files/SSError.txt中 | |
81 | + // 开启错误日志保存到本地,发生错误时文件中会保存到android/data/包名/files/SSError.txt中 | |
83 | 82 | // setOpenWriteLog(true) |
84 | - // 设置在线服务器地址和账号 | |
85 | - setServerAPI("wss://api.cloud.ssapi.cn") | |
83 | + // 设置在线服务器地址和账号 | |
84 | + setServerAPI("wss://api.cloud.ssapi.cn") | |
86 | 85 | // // 设置评测语言(针对离线评测) |
87 | 86 | // setOffLineSource(OffLineSourceEnum.SOURCE_EN) |
88 | - // 设置引擎初始化参数 | |
89 | - setNewCfg( | |
90 | - buildInitJson( | |
91 | - SingSoundConfig.APPKEY, | |
92 | - SingSoundConfig.SECERTKEY | |
87 | + // 设置引擎初始化参数 | |
88 | + setNewCfg( | |
89 | + buildInitJson( | |
90 | + SingSoundConfig.APPKEY, | |
91 | + SingSoundConfig.SECERTKEY | |
92 | + ) | |
93 | 93 | ) |
94 | - ) | |
95 | - // 引擎初始化 | |
96 | - createEngine() | |
97 | - } | |
94 | + // 引擎初始化 | |
95 | + createEngine("1") | |
98 | 96 | |
99 | - getSymbolsMap() | |
100 | - } catch (e: Exception) { | |
101 | - e.printStackTrace() | |
102 | - } | |
103 | - }.start() | |
97 | + Log.w(TAG, "createEngine") | |
98 | + } | |
99 | + | |
100 | + getSymbolsMap() | |
101 | + } catch (e: Exception) { | |
102 | + e.printStackTrace() | |
103 | + } | |
104 | + }.start() | |
105 | + } | |
104 | 106 | } |
105 | 107 | |
106 | 108 | // 开始语音评测 |
107 | - fun startRecord(originText: String, @EvalTargetType evalTargetType: Int?) { | |
109 | + fun startRecord(originText: String, @EvalTargetType evalTargetType: Int? = EvalTargetType.SENTENCE) { | |
108 | 110 | try { |
109 | 111 | val request = JSONObject() |
110 | 112 | when (evalTargetType) { | ... | ... |
lib/generated/json/course_process_entity.g.dart
... | ... | @@ -38,9 +38,9 @@ Map<String, dynamic> $CourseProcessEntityToJson(CourseProcessEntity entity) { |
38 | 38 | |
39 | 39 | CourseProcessReadings $CourseProcessReadingsFromJson(Map<String, dynamic> json) { |
40 | 40 | final CourseProcessReadings courseProcessReadings = CourseProcessReadings(); |
41 | - final String? auditUrl = jsonConvert.convert<String>(json['auditUrl']); | |
42 | - if (auditUrl != null) { | |
43 | - courseProcessReadings.auditUrl = auditUrl; | |
41 | + final String? audioUrl = jsonConvert.convert<String>(json['audioUrl']); | |
42 | + if (audioUrl != null) { | |
43 | + courseProcessReadings.audioUrl = audioUrl; | |
44 | 44 | } |
45 | 45 | final int? courseLessonId = jsonConvert.convert<int>(json['courseLessonId']); |
46 | 46 | if (courseLessonId != null) { |
... | ... | @@ -83,7 +83,7 @@ CourseProcessReadings $CourseProcessReadingsFromJson(Map<String, dynamic> json) |
83 | 83 | |
84 | 84 | Map<String, dynamic> $CourseProcessReadingsToJson(CourseProcessReadings entity) { |
85 | 85 | final Map<String, dynamic> data = <String, dynamic>{}; |
86 | - data['auditUrl'] = entity.auditUrl; | |
86 | + data['audioUrl'] = entity.audioUrl; | |
87 | 87 | data['courseLessonId'] = entity.courseLessonId; |
88 | 88 | data['createTime'] = entity.createTime; |
89 | 89 | data['deleted'] = entity.deleted; | ... | ... |
lib/models/course_process_entity.dart
lib/pages/practice/bloc/topic_picture_bloc.dart
... | ... | @@ -142,7 +142,7 @@ class TopicPictureBloc extends Bloc<TopicPictureEvent, TopicPictureState> { |
142 | 142 | void _voiceXsTest(XSVoiceTestEvent event,Emitter<TopicPictureState> emitter) async { |
143 | 143 | EasyLoading.show(status: '录音中....'); |
144 | 144 | methodChannel.invokeMethod( |
145 | - 'starVoice', | |
145 | + 'startVoice', | |
146 | 146 | {'word':event.testWord,'type':event.type,'userId':event.userId.toString()} |
147 | 147 | ); |
148 | 148 | _isVoicing = true; | ... | ... |
lib/pages/reading/bloc/reading_bloc.dart
1 | +import 'package:audioplayers/audioplayers.dart'; | |
1 | 2 | import 'package:flutter/cupertino.dart'; |
3 | +import 'package:flutter/foundation.dart'; | |
4 | +import 'package:flutter/services.dart'; | |
2 | 5 | import 'package:flutter_bloc/flutter_bloc.dart'; |
6 | +import 'package:flutter_easyloading/flutter_easyloading.dart'; | |
3 | 7 | import 'package:wow_english/pages/reading/widgets/ReadingModeType.dart'; |
4 | 8 | |
9 | +import '../../../common/request/dao/listen_dao.dart'; | |
10 | +import '../../../common/request/exception.dart'; | |
11 | +import '../../../models/course_process_entity.dart'; | |
12 | +import '../../../utils/loading.dart'; | |
13 | + | |
5 | 14 | part 'reading_event.dart'; |
6 | 15 | part 'reading_state.dart'; |
7 | 16 | |
8 | 17 | class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
9 | - | |
10 | 18 | final PageController pageController; |
11 | 19 | |
12 | 20 | ///当前页索引 |
... | ... | @@ -19,26 +27,68 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
19 | 27 | |
20 | 28 | ReadingModeType get currentMode => _currentMode; |
21 | 29 | |
30 | + CourseProcessEntity? _entity; | |
31 | + | |
32 | + CourseProcessEntity? get entity => _entity; | |
33 | + | |
34 | + ///正在评测 | |
35 | + bool _isRecording = false; | |
36 | + | |
37 | + bool get isRecording => _isRecording; | |
38 | + | |
39 | + late MethodChannel methodChannel; | |
40 | + | |
41 | + late AudioPlayer audioPlayer; | |
42 | + | |
22 | 43 | ReadingPageBloc(this.pageController) : super(ReadingPageInitial()) { |
23 | 44 | on<CurrentPageIndexChangeEvent>(_pageControllerChange); |
24 | 45 | on<CurrentModeChangeEvent>(_selectItemLoad); |
25 | 46 | // pageController.addListener(() { |
26 | 47 | // _currentPage = pageController.page!.round(); |
27 | 48 | // }); |
49 | + on<RequestDataEvent>(_requestData); | |
50 | + on<ReadingPageEvent>((event, emit) { | |
51 | + //音频播放器 | |
52 | + audioPlayer = AudioPlayer(); | |
53 | + audioPlayer.onPlayerStateChanged.listen((event) { | |
54 | + if (event == PlayerState.completed) { | |
55 | + if (kDebugMode) { | |
56 | + print('绘本播放完成'); | |
57 | + | |
58 | + } | |
59 | + } | |
60 | + }); | |
61 | + | |
62 | + methodChannel = const MethodChannel('sing_sound_method_channel'); | |
63 | + methodChannel.invokeMethod('initVoiceSdk',{}); | |
64 | + methodChannel.setMethodCallHandler((call) async { | |
65 | + if (call.method == 'voiceResult') {//评测结束 | |
66 | + // add(XSVoiceResultEvent(call.arguments)); | |
67 | + } | |
68 | + }); | |
69 | + }); | |
70 | + on<PlayOriginalAudioEvent>(_playOriginalAudio); | |
71 | + on<XSVoiceTestEvent>(_voiceXsTest); | |
72 | + on<XSVoiceResultEvent>(_voiceXsResult); | |
28 | 73 | } |
29 | 74 | |
30 | 75 | @override |
31 | 76 | Future<void> close() { |
32 | 77 | pageController.dispose(); |
78 | + audioPlayer.release(); | |
79 | + audioPlayer.dispose(); | |
33 | 80 | return super.close(); |
34 | 81 | } |
35 | 82 | |
36 | - void _pageControllerChange(CurrentPageIndexChangeEvent event, Emitter<ReadingPageState> emitter) async { | |
83 | + void _pageControllerChange(CurrentPageIndexChangeEvent event, | |
84 | + Emitter<ReadingPageState> emitter) async { | |
37 | 85 | _currentPage = event.pageIndex; |
86 | + _playOriginVoice(null); | |
38 | 87 | emitter(CurrentPageIndexState()); |
39 | 88 | } |
40 | 89 | |
41 | - void _selectItemLoad(CurrentModeChangeEvent event, Emitter<ReadingPageState> emitter) async { | |
90 | + void _selectItemLoad( | |
91 | + CurrentModeChangeEvent event, Emitter<ReadingPageState> emitter) async { | |
42 | 92 | if (_currentMode == ReadingModeType.auto) { |
43 | 93 | _currentMode = ReadingModeType.manual; |
44 | 94 | } else { |
... | ... | @@ -46,4 +96,77 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
46 | 96 | } |
47 | 97 | emitter(CurrentModeState()); |
48 | 98 | } |
99 | + | |
100 | + ///请求数据 | |
101 | + void _requestData( | |
102 | + RequestDataEvent event, Emitter<ReadingPageState> emitter) async { | |
103 | + try { | |
104 | + await loading(() async { | |
105 | + _entity = await ListenDao.process('1'); | |
106 | + print("reading page entity: ${_entity!.toJson()}"); | |
107 | + emitter(RequestDataState()); | |
108 | + }); | |
109 | + } catch (e) { | |
110 | + if (e is ApiException) { | |
111 | + EasyLoading.showToast(e.message ?? '请求失败,请检查网络连接'); | |
112 | + } | |
113 | + } | |
114 | + } | |
115 | + | |
116 | + void _playOriginalAudio(PlayOriginalAudioEvent event, Emitter<ReadingPageState> emitter) async { | |
117 | + print("_playOriginalAudio"); | |
118 | + _playOriginVoice(event.url); | |
119 | + } | |
120 | + | |
121 | + /// 播放绘本原音 | |
122 | + void _playOriginVoice(String? audioUrl) async { | |
123 | + audioPlayer.stop(); | |
124 | + final readingData = currentPageData(); | |
125 | + if (readingData?.audioUrl != null) { | |
126 | + final urlStr = audioUrl ?? readingData?.audioUrl ?? ''; | |
127 | + if (urlStr.isNotEmpty) { | |
128 | + audioPlayer.play(UrlSource(urlStr)); | |
129 | + } | |
130 | + } | |
131 | + } | |
132 | + | |
133 | + int dataCount() { | |
134 | + // print("dataCount=${_entity?.readings?.length ?? 0}"); | |
135 | + return _entity?.readings?.length ?? 0; | |
136 | + } | |
137 | + | |
138 | + CourseProcessReadings? currentPageData() { | |
139 | + return _entity?.readings?[_currentPage]; | |
140 | + } | |
141 | + | |
142 | + ///先声测试 | |
143 | + void _voiceXsTest(XSVoiceTestEvent event, Emitter<ReadingPageState> emitter) async { | |
144 | + startRecord(event.content); | |
145 | + emitter(XSVoiceTestState()); | |
146 | + } | |
147 | + | |
148 | + void startRecord(String content) async { | |
149 | + if (_isRecording == true) { | |
150 | + return; | |
151 | + } | |
152 | + EasyLoading.show(status: '录音中....'); | |
153 | + methodChannel.invokeMethod( | |
154 | + 'startVoice', | |
155 | + {'word':'how old are you','type':'0','userId':'1'} | |
156 | + ); | |
157 | + _isRecording = true; | |
158 | + } | |
159 | + | |
160 | + void _voiceXsResult(XSVoiceResultEvent event,Emitter<ReadingPageState> emitter) async { | |
161 | + final Map args = event.message as Map; | |
162 | + final result = args['result'] as String; | |
163 | + if (result == '1') { | |
164 | + final overall = args['overall'].toString(); | |
165 | + EasyLoading.showToast('测评成功,分数是$overall',duration: const Duration(seconds: 10)); | |
166 | + } else { | |
167 | + EasyLoading.showToast('测评失败',duration: const Duration(seconds: 10)); | |
168 | + } | |
169 | + _isRecording = false; | |
170 | + emitter(XSVoiceTestState()); | |
171 | + } | |
49 | 172 | } | ... | ... |
lib/pages/reading/bloc/reading_event.dart
... | ... | @@ -8,4 +8,33 @@ class CurrentPageIndexChangeEvent extends ReadingPageEvent { |
8 | 8 | CurrentPageIndexChangeEvent(this.pageIndex); |
9 | 9 | } |
10 | 10 | |
11 | -class CurrentModeChangeEvent extends ReadingPageEvent {} | |
12 | 11 | \ No newline at end of file |
12 | +class CurrentModeChangeEvent extends ReadingPageEvent {} | |
13 | + | |
14 | +///请求接口获取数据 | |
15 | +class RequestDataEvent extends ReadingPageEvent {} | |
16 | + | |
17 | +///播放原音频 | |
18 | +class PlayOriginalAudioEvent extends ReadingPageEvent { | |
19 | + final String? url; | |
20 | + PlayOriginalAudioEvent(this.url); | |
21 | +} | |
22 | + | |
23 | +///初始化先声SDK | |
24 | +class XSVoiceInitEvent extends ReadingPageEvent { | |
25 | + final Map data; | |
26 | + XSVoiceInitEvent(this.data); | |
27 | +} | |
28 | + | |
29 | +///评测结果 | |
30 | +class XSVoiceResultEvent extends ReadingPageEvent { | |
31 | + final dynamic message; | |
32 | + XSVoiceResultEvent(this.message); | |
33 | +} | |
34 | + | |
35 | +///先声测试 | |
36 | +class XSVoiceTestEvent extends ReadingPageEvent { | |
37 | + final String content; | |
38 | + final String type; | |
39 | + final String userId; | |
40 | + XSVoiceTestEvent(this.content,this.type,this.userId); | |
41 | +} | |
13 | 42 | \ No newline at end of file | ... | ... |
lib/pages/reading/bloc/reading_state.dart
lib/pages/reading/reading_page.dart
... | ... | @@ -4,6 +4,8 @@ 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 | 6 | |
7 | +import '../../common/core/user_util.dart'; | |
8 | +import '../../models/course_process_entity.dart'; | |
7 | 9 | import 'bloc/reading_bloc.dart'; |
8 | 10 | |
9 | 11 | class ReadingPage extends StatelessWidget { |
... | ... | @@ -12,7 +14,7 @@ class ReadingPage extends StatelessWidget { |
12 | 14 | @override |
13 | 15 | Widget build(BuildContext context) { |
14 | 16 | return BlocProvider( |
15 | - create: (_) => ReadingPageBloc(PageController()), | |
17 | + create: (_) => ReadingPageBloc(PageController())..add(RequestDataEvent()), | |
16 | 18 | child: _ReadingPage(), |
17 | 19 | ); |
18 | 20 | } |
... | ... | @@ -22,7 +24,15 @@ class _ReadingPage extends StatelessWidget { |
22 | 24 | @override |
23 | 25 | Widget build(BuildContext context) { |
24 | 26 | return BlocListener<ReadingPageBloc, ReadingPageState>( |
25 | - listener: (context, state) {}, | |
27 | + listener: (context, state) { | |
28 | + if (state is RequestDataState) { | |
29 | + // context.read<TopicPictureBloc>().add(CurrentPageIndexChangeEvent(0)); | |
30 | + print('reading RequestDataState=$state'); | |
31 | + | |
32 | + ///刷新页面 | |
33 | + context.read<ReadingPageBloc>().add(CurrentPageIndexChangeEvent(0)); | |
34 | + } | |
35 | + }, | |
26 | 36 | child: _readingPageView(), |
27 | 37 | ); |
28 | 38 | } |
... | ... | @@ -36,13 +46,13 @@ class _ReadingPage extends StatelessWidget { |
36 | 46 | child: Stack( |
37 | 47 | children: [ |
38 | 48 | PageView.builder( |
39 | - itemCount: 10, | |
49 | + itemCount: bloc.dataCount(), | |
40 | 50 | controller: bloc.pageController, |
41 | 51 | onPageChanged: (int index) { |
42 | 52 | bloc.add(CurrentPageIndexChangeEvent(index)); |
43 | 53 | }, |
44 | 54 | itemBuilder: (context, int index) { |
45 | - return _readingPagerItem(); | |
55 | + return _readingPagerItem(bloc.entity!.readings![index]); | |
46 | 56 | }), |
47 | 57 | Container( |
48 | 58 | color: Colors.transparent, |
... | ... | @@ -76,15 +86,14 @@ class _ReadingPage extends StatelessWidget { |
76 | 86 | ), |
77 | 87 | alignment: Alignment.center, |
78 | 88 | child: Text( |
79 | - '${bloc.currentPage}/10', | |
80 | - | |
81 | - ///todo 分母需要替换成数据数组长度 | |
89 | + '${bloc.currentPage}/${bloc.dataCount()}', | |
82 | 90 | style: TextStyle(fontSize: 20.sp, color: Colors.white), |
83 | 91 | ), |
84 | 92 | ), |
85 | 93 | |
86 | 94 | Padding( |
87 | - padding: EdgeInsets.only(right: 15.w + ScreenUtil().bottomBarHeight), | |
95 | + padding: EdgeInsets.only( | |
96 | + right: 15.w + ScreenUtil().bottomBarHeight), | |
88 | 97 | child: GestureDetector( |
89 | 98 | onTap: () { |
90 | 99 | bloc.add(CurrentModeChangeEvent()); |
... | ... | @@ -121,17 +130,26 @@ class _ReadingPage extends StatelessWidget { |
121 | 130 | margin: EdgeInsets.symmetric(horizontal: 10.w), |
122 | 131 | child: Row( |
123 | 132 | children: [ |
124 | - Image.asset( | |
125 | - 'voice'.assetPng, | |
126 | - height: 40.h, | |
127 | - width: 45.w, | |
133 | + GestureDetector( | |
134 | + onTap: () { | |
135 | + if (bloc.isRecording) { | |
136 | + return; | |
137 | + } | |
138 | + print("voice tap"); | |
139 | + bloc.add(PlayOriginalAudioEvent(null)); | |
140 | + }, | |
141 | + child: Image.asset( | |
142 | + 'voice'.assetPng, | |
143 | + height: 40.h, | |
144 | + width: 45.w, | |
145 | + ), | |
128 | 146 | ), |
129 | 147 | SizedBox( |
130 | 148 | width: 10.w, |
131 | 149 | ), |
132 | 150 | Expanded( |
133 | 151 | child: Text( |
134 | - "HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorld", | |
152 | + bloc.currentPageData()?.word ?? '', | |
135 | 153 | style: TextStyle( |
136 | 154 | color: const Color(0xFF333333), fontSize: 21.sp), |
137 | 155 | maxLines: 2, |
... | ... | @@ -140,11 +158,18 @@ class _ReadingPage extends StatelessWidget { |
140 | 158 | SizedBox( |
141 | 159 | width: 10.w, |
142 | 160 | ), |
143 | - Image.asset( | |
144 | - 'micro_phone'.assetPng, | |
145 | - height: 47.h, | |
146 | - width: 47.w, | |
147 | - ), | |
161 | + GestureDetector( | |
162 | + onTap: () { | |
163 | + if (bloc.isRecording) { | |
164 | + return; | |
165 | + } | |
166 | + bloc.add(XSVoiceTestEvent(bloc.currentPageData()?.word??'', '0',UserUtil.getUser()!.id.toString())); | |
167 | + }, | |
168 | + child: Image.asset( | |
169 | + 'micro_phone'.assetPng, | |
170 | + height: 47.h, | |
171 | + width: 47.w, | |
172 | + )), | |
148 | 173 | SizedBox( |
149 | 174 | width: 10.w, |
150 | 175 | ), |
... | ... | @@ -169,14 +194,12 @@ class _ReadingPage extends StatelessWidget { |
169 | 194 | ); |
170 | 195 | }); |
171 | 196 | |
172 | - Widget _readingPagerItem() => | |
197 | + Widget _readingPagerItem(CourseProcessReadings readings) => | |
173 | 198 | BlocBuilder<ReadingPageBloc, ReadingPageState>(builder: (context, state) { |
174 | 199 | return Stack( |
175 | 200 | children: [ |
176 | - Image.network( | |
177 | - 'https://img.liblibai.com/web/648331d5a2cb5.png?image_process=format,webp&x-oss-process=image/resize,w_2980,m_lfit/format,webp', | |
178 | - height: double.infinity, | |
179 | - width: double.infinity), | |
201 | + Image.network(readings.picUrl ?? '', | |
202 | + height: double.infinity, width: double.infinity), | |
180 | 203 | ], |
181 | 204 | ); |
182 | 205 | }); | ... | ... |