2eb67dd4
liangchengyou
feat:调整代码
|
1
|
import 'package:audioplayers/audioplayers.dart';
|
688b7b5c
liangchengyou
feat:更新文件结构
|
2
|
import 'package:flutter/cupertino.dart';
|
2eb67dd4
liangchengyou
feat:调整代码
|
3
4
|
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
|
688b7b5c
liangchengyou
feat:更新文件结构
|
5
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
2eb67dd4
liangchengyou
feat:调整代码
|
6
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
354ac7e6
吴启风
feat:隐藏视频跟读入口、底部草...
|
7
|
import 'package:permission_handler/permission_handler.dart';
|
2eb67dd4
liangchengyou
feat:调整代码
|
8
9
10
11
|
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';
import 'package:wow_english/utils/loading.dart';
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
12
|
import 'package:wow_english/utils/toast_util.dart';
|
7652f701
liangchengyou
feat:课程购买UI逻辑
|
13
|
|
354ac7e6
吴启风
feat:隐藏视频跟读入口、底部草...
|
14
15
|
import '../../../common/permission/permissionRequestPage.dart';
|
7652f701
liangchengyou
feat:课程购买UI逻辑
|
16
|
part 'topic_picture_event.dart';
|
688b7b5c
liangchengyou
feat:更新文件结构
|
17
|
part 'topic_picture_state.dart';
|
7652f701
liangchengyou
feat:课程购买UI逻辑
|
18
|
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
19
20
21
22
23
24
25
26
27
28
29
|
enum VoicePlayState {
///未知
unKnow,
///播放中
playing,
///播放完成
completed,
///播放终止
stop
}
|
7652f701
liangchengyou
feat:课程购买UI逻辑
|
30
|
class TopicPictureBloc extends Bloc<TopicPictureEvent, TopicPictureState> {
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
31
32
33
|
final PageController pageController;
|
608c05b4
liangchengyou
feat:兑换课程
|
34
|
final String courseLessonId;
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
35
36
37
|
int _currentPage = 0;
|
2eb67dd4
liangchengyou
feat:调整代码
|
38
39
40
41
42
43
44
|
int _selectItem = -1;
CourseProcessEntity? _entity;
///正在评测
bool _isVoicing = false;
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
45
46
47
|
///正在播放音频
VoicePlayState _voicePlayState = VoicePlayState.unKnow;
|
2eb67dd4
liangchengyou
feat:调整代码
|
48
|
CourseProcessEntity? get entity => _entity;
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
49
50
51
52
53
|
int get currentPage => _currentPage + 1;
int get selectItem => _selectItem;
|
2eb67dd4
liangchengyou
feat:调整代码
|
54
55
|
bool get isVoicing => _isVoicing;
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
56
57
|
VoicePlayState get voicePlayState => _voicePlayState;
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
58
|
late MethodChannel methodChannel;
|
2eb67dd4
liangchengyou
feat:调整代码
|
59
|
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
60
|
late AudioPlayer audioPlayer;
|
2eb67dd4
liangchengyou
feat:调整代码
|
61
|
|
354ac7e6
吴启风
feat:隐藏视频跟读入口、底部草...
|
62
63
64
|
final BuildContext context;
TopicPictureBloc(this.context, this.pageController, this.courseLessonId) : super(TopicPictureInitial()) {
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
65
|
on<CurrentPageIndexChangeEvent>(_pageControllerChange);
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
66
67
68
|
on<VoicePlayStateChangeEvent>(_voicePlayStateChange);
on<XSVoiceResultEvent>(_voiceXsResult);
on<XSVoiceInitEvent>(_initVoiceSdk);
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
69
|
on<SelectItemEvent>(_selectItemLoad);
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
70
71
|
on<RequestDataEvent>(_requestData);
on<XSVoiceTestEvent>(_voiceXsTest);
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
72
73
74
|
on<XSVoiceStopEvent>(_voiceXsStop);
on<VoicePlayEvent>(_voicePlay);
on<InitBlocEvent>((event, emit) {
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
75
76
|
//音频播放器
audioPlayer = AudioPlayer();
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
77
78
|
audioPlayer.onPlayerStateChanged.listen((event) async {
debugPrint('播放状态变化');
|
b90a1518
liangchengyou
feat:练习接口逻辑完成
|
79
|
if (event == PlayerState.completed) {
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
80
81
82
83
84
85
86
|
debugPrint('播放完成');
_voicePlayState = VoicePlayState.completed;
}
if (event == PlayerState.stopped) {
debugPrint('播放结束');
_voicePlayState = VoicePlayState.stop;
}
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
87
|
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
88
89
90
91
92
93
94
95
|
if (event == PlayerState.playing) {
debugPrint('正在播放中');
_voicePlayState = VoicePlayState.playing;
}
if(isClosed) {
return;
}
add(VoicePlayStateChangeEvent());
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
96
97
|
});
|
53e9e6db
吴启风
feat:绘本语音评测逻辑
|
98
|
methodChannel = const MethodChannel('wow_english/sing_sound_method_channel');
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
99
|
methodChannel.setMethodCallHandler((call) async {
|
b90a1518
liangchengyou
feat:练习接口逻辑完成
|
100
|
if (call.method == 'voiceResult') {//评测结果
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
101
|
add(XSVoiceResultEvent(call.arguments));
|
b90a1518
liangchengyou
feat:练习接口逻辑完成
|
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
return;
}
if (call.method == 'voiceStart') {//评测开始
if (kDebugMode) {
print('评测开始');
}
return;
}
if (call.method == 'voiceEnd') {//评测结束
if (kDebugMode) {
print('评测结束');
}
return;
}
if (call.method == 'voiceFail') {//评测失败
EasyLoading.showToast('评测失败');
return;
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
122
123
|
}
});
|
2eb67dd4
liangchengyou
feat:调整代码
|
124
|
});
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
125
126
127
|
}
@override
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
128
|
Future<void> close(){
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
129
|
pageController.dispose();
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
130
|
audioPlayer.release();
|
2eb67dd4
liangchengyou
feat:调整代码
|
131
|
audioPlayer.dispose();
|
6b0947ca
吴启风
feat:绘本增加initVoic...
|
132
|
_voiceXsCancel();
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
133
134
135
|
return super.close();
}
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
136
|
///请求数据
|
2eb67dd4
liangchengyou
feat:调整代码
|
137
138
139
|
void _requestData(RequestDataEvent event,Emitter<TopicPictureState> emitter) async {
try {
await loading(() async {
|
608c05b4
liangchengyou
feat:兑换课程
|
140
|
_entity = await ListenDao.process(courseLessonId);
|
2eb67dd4
liangchengyou
feat:调整代码
|
141
142
143
144
|
emitter(RequestDataState());
});
} catch (e) {
if (e is ApiException) {
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
145
|
showToast(e.message??'请求失败,请检查网络连接');
|
2eb67dd4
liangchengyou
feat:调整代码
|
146
147
148
149
|
}
}
}
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
150
|
///页面切换
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
151
152
|
void _pageControllerChange(CurrentPageIndexChangeEvent event,Emitter<TopicPictureState> emitter) async {
_currentPage = event.pageIndex;
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
153
154
155
|
if (voicePlayState == VoicePlayState.playing) {
await audioPlayer.stop();
}
|
2eb67dd4
liangchengyou
feat:调整代码
|
156
|
final topics = _entity?.topics?[_currentPage];
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
157
|
if (topics?.type != 3 && topics?.type != 4) {
|
2eb67dd4
liangchengyou
feat:调整代码
|
158
159
160
|
if (topics?.audioUrl != null) {
final urlStr = topics?.audioUrl??'';
if (urlStr.isNotEmpty) {
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
161
162
|
debugPrint(urlStr);
await audioPlayer.play(UrlSource(urlStr));
|
2eb67dd4
liangchengyou
feat:调整代码
|
163
164
|
}
}
|
2eb67dd4
liangchengyou
feat:调整代码
|
165
|
}
|
b90a1518
liangchengyou
feat:练习接口逻辑完成
|
166
|
_selectItem = -1;
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
167
168
169
|
emitter(CurrentPageIndexState());
}
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
170
|
///选择
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
171
172
|
void _selectItemLoad(SelectItemEvent event,Emitter<TopicPictureState> emitter) async {
_selectItem = event.selectIndex;
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
173
174
175
176
177
178
179
|
CourseProcessTopics? topics = _entity?.topics?[_currentPage];
CourseProcessTopicsTopicAnswerList? answerList = topics?.topicAnswerList?[_selectItem];
if (answerList?.correct == 0) {
showToast('继续加油哦',duration: const Duration(seconds: 2));
} else {
showToast('恭喜你,答对啦!',duration: const Duration(seconds: 2));
}
|
624214d0
liangchengyou
feat:看题选字/选图UI和部分逻辑
|
180
|
emitter(SelectItemChangeState());
|
7652f701
liangchengyou
feat:课程购买UI逻辑
|
181
|
}
|
2eb67dd4
liangchengyou
feat:调整代码
|
182
|
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
183
184
185
186
187
|
///初始化SDK
_initVoiceSdk(XSVoiceInitEvent event,Emitter<TopicPictureState> emitter) async {
methodChannel.invokeMethod('initVoiceSdk',event.data);
}
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
188
189
|
///先声测试
void _voiceXsTest(XSVoiceTestEvent event,Emitter<TopicPictureState> emitter) async {
|
53e9e6db
吴启风
feat:绘本语音评测逻辑
|
190
|
await audioPlayer.stop();
|
354ac7e6
吴启风
feat:隐藏视频跟读入口、底部草...
|
191
192
193
194
195
|
// 调用封装好的权限检查和请求方法
bool result = await permissionCheckAndRequest(
context,
Permission.microphone,
"录音"
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
196
|
);
|
354ac7e6
吴启风
feat:隐藏视频跟读入口、底部草...
|
197
198
199
200
201
202
203
204
205
206
207
208
|
if (result) {
methodChannel.invokeMethod(
'startVoice',
{
'word': event.testWord,
'type': event.type,
'userId': event.userId.toString()
}
);
_isVoicing = true;
emitter(XSVoiceTestState());
}
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
209
210
|
}
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
211
212
213
214
215
|
///终止评测
void _voiceXsStop(XSVoiceStopEvent event,Emitter<TopicPictureState> emitter) async {
methodChannel.invokeMethod('stopVoice');
}
|
6b0947ca
吴启风
feat:绘本增加initVoic...
|
216
217
218
219
220
|
///取消评测(用于处理退出页面后录音未停止等异常情况的保护操作)
void _voiceXsCancel() {
methodChannel.invokeMethod('cancelVoice');
}
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
221
|
///先声评测结果
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
222
223
|
void _voiceXsResult(XSVoiceResultEvent event,Emitter<TopicPictureState> emitter) async {
final Map args = event.message as Map;
|
cb38bc90
liangchengyou
feat:视频跟读逻辑处理
|
224
225
226
|
final result = args['result'] as Map;
final overall = result['overall'].toString();
showToast('测评成功,分数是$overall',duration: const Duration(seconds: 5));
|
2eb67dd4
liangchengyou
feat:调整代码
|
227
|
_isVoicing = false;
|
e3c2820c
liangchengyou
feat:先声SDK逻辑调整
|
228
|
emitter(XSVoiceTestState());
|
2eb67dd4
liangchengyou
feat:调整代码
|
229
|
}
|
3c1d5c64
liangchengyou
feat:练习功能完成
|
230
231
232
233
234
235
236
237
238
239
240
241
242
243
|
void _voicePlayStateChange(VoicePlayStateChangeEvent event,Emitter<TopicPictureState> emitter) async {
emitter(VoicePlayStateChange());
}
void _voicePlay(VoicePlayEvent event,Emitter<TopicPictureState> emitter) async {
if (voicePlayState == VoicePlayState.playing) {
await audioPlayer.stop();
return;
}
final topics = _entity?.topics?[_currentPage];
final urlStr = topics?.audioUrl??'';
await audioPlayer.play(UrlSource(urlStr));
}
|
7652f701
liangchengyou
feat:课程购买UI逻辑
|
244
|
}
|