Commit 3c1d5c64b66a976b75f76c9efe5dec1553e82c93

Authored by liangchengyou
1 parent a506beff

feat:练习功能完成

assets/images/micro_phone.gif 0 → 100644

17.3 KB

assets/images/reade_answer.gif 0 → 100644

5.31 KB

ios/Runner/XSMessageMehtodChannel.swift
@@ -39,7 +39,7 @@ class XSMessageMehtodChannel: NSObject,SSOralEvaluatingManagerDelegate { @@ -39,7 +39,7 @@ class XSMessageMehtodChannel: NSObject,SSOralEvaluatingManagerDelegate {
39 } 39 }
40 40
41 //开始评测 41 //开始评测
42 - func evaluateVioce(dict:Dictionary<String, Any>) { 42 + func evaluateVoice(dict:Dictionary<String, Any>) {
43 let text = dict["word"] as? String ?? "" 43 let text = dict["word"] as? String ?? ""
44 let type = dict["type"] as? String ?? "0" 44 let type = dict["type"] as? String ?? "0"
45 let userId = dict["userId"] as? String ?? "guest" 45 let userId = dict["userId"] as? String ?? "guest"
@@ -50,6 +50,7 @@ class XSMessageMehtodChannel: NSObject,SSOralEvaluatingManagerDelegate { @@ -50,6 +50,7 @@ class XSMessageMehtodChannel: NSObject,SSOralEvaluatingManagerDelegate {
50 } else { 50 } else {
51 config.oralType = .sentence 51 config.oralType = .sentence
52 } 52 }
  53 + config.oralType = .kidSent
53 config.userId = userId 54 config.userId = userId
54 SSOralEvaluatingManager.share().startEvaluateOral(with: config) 55 SSOralEvaluatingManager.share().startEvaluateOral(with: config)
55 } 56 }
@@ -59,8 +60,8 @@ class XSMessageMehtodChannel: NSObject,SSOralEvaluatingManagerDelegate { @@ -59,8 +60,8 @@ class XSMessageMehtodChannel: NSObject,SSOralEvaluatingManagerDelegate {
59 self.setEvaluateConfig(dict:call.arguments as! Dictionary<String, Any>) 60 self.setEvaluateConfig(dict:call.arguments as! Dictionary<String, Any>)
60 return 61 return
61 } 62 }
62 - if (call.method == "starVoice") {  
63 - self.evaluateVioce(dict: call.arguments as! Dictionary<String, Any>) 63 + if (call.method == "startVoice") {
  64 + self.evaluateVoice(dict: call.arguments as! Dictionary<String, Any>)
64 return 65 return
65 } 66 }
66 67
lib/pages/home/bloc/home_bloc.dart
@@ -4,6 +4,8 @@ import &#39;package:flutter_bloc/flutter_bloc.dart&#39;; @@ -4,6 +4,8 @@ import &#39;package:flutter_bloc/flutter_bloc.dart&#39;;
4 import 'package:wow_english/common/request/dao/home_dao.dart'; 4 import 'package:wow_english/common/request/dao/home_dao.dart';
5 import 'package:wow_english/common/request/exception.dart'; 5 import 'package:wow_english/common/request/exception.dart';
6 import 'package:wow_english/models/course_entity.dart'; 6 import 'package:wow_english/models/course_entity.dart';
  7 +import 'package:wow_english/common/request/dao/listen_dao.dart';
  8 +import 'package:wow_english/models/course_process_entity.dart';
7 // import 'package:wow_english/models/course_lesson_entity.dart'; 9 // import 'package:wow_english/models/course_lesson_entity.dart';
8 import 'package:wow_english/utils/loading.dart'; 10 import 'package:wow_english/utils/loading.dart';
9 import 'package:wow_english/utils/toast_util.dart'; 11 import 'package:wow_english/utils/toast_util.dart';
@@ -18,8 +20,13 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; { @@ -18,8 +20,13 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; {
18 20
19 CourseEntity? get modelData => _modelData; 21 CourseEntity? get modelData => _modelData;
20 22
  23 + CourseProcessEntity? _processEntity;
  24 +
  25 + CourseProcessEntity? get processEntity => _processEntity;
  26 +
21 HomeBloc(this.moduleId) : super(HomeInitial()) { 27 HomeBloc(this.moduleId) : super(HomeInitial()) {
22 on<RequestDataEvent>(_requestData); 28 on<RequestDataEvent>(_requestData);
  29 + on<RequestVideoLessonEvent>(_requestVideoLesson);
23 } 30 }
24 31
25 void _requestData(RequestDataEvent event, Emitter<HomeState> emitter) async { 32 void _requestData(RequestDataEvent event, Emitter<HomeState> emitter) async {
@@ -34,4 +41,17 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; { @@ -34,4 +41,17 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; {
34 } 41 }
35 } 42 }
36 } 43 }
  44 +
  45 + void _requestVideoLesson(RequestVideoLessonEvent event, Emitter<HomeState> emitter) async {
  46 + try {
  47 + await loading(() async {
  48 + _processEntity = await ListenDao.process(event.courseLessonId);
  49 + emitter(RequestVideoLessonState(event.courseType));
  50 + });
  51 + } catch (e) {
  52 + if (e is ApiException) {
  53 + showToast(e.message??'请求失败,请检查网络连接');
  54 + }
  55 + }
  56 + }
37 } 57 }
lib/pages/home/bloc/home_event.dart
@@ -4,3 +4,10 @@ part of &#39;home_bloc.dart&#39;; @@ -4,3 +4,10 @@ part of &#39;home_bloc.dart&#39;;
4 abstract class HomeEvent {} 4 abstract class HomeEvent {}
5 5
6 class RequestDataEvent extends HomeEvent {} 6 class RequestDataEvent extends HomeEvent {}
  7 +
  8 +///获取视频课程内容
  9 +class RequestVideoLessonEvent extends HomeEvent {
  10 + final String courseLessonId;
  11 + final int courseType;
  12 + RequestVideoLessonEvent(this.courseLessonId, this.courseType);
  13 +}
lib/pages/home/bloc/home_state.dart
@@ -6,3 +6,8 @@ abstract class HomeState {} @@ -6,3 +6,8 @@ abstract class HomeState {}
6 class HomeInitial extends HomeState {} 6 class HomeInitial extends HomeState {}
7 7
8 class HomeDataLoadState extends HomeState {} 8 class HomeDataLoadState extends HomeState {}
  9 +
  10 +class RequestVideoLessonState extends HomeState {
  11 + final int type;
  12 + RequestVideoLessonState(this.type);
  13 +}
lib/pages/home/home_page.dart
@@ -45,8 +45,30 @@ class _HomePageView extends StatelessWidget { @@ -45,8 +45,30 @@ class _HomePageView extends StatelessWidget {
45 45
46 @override 46 @override
47 Widget build(BuildContext context) { 47 Widget build(BuildContext context) {
  48 + final bloc = BlocProvider.of<HomeBloc>(context);
48 return BlocListener<HomeBloc, HomeState>( 49 return BlocListener<HomeBloc, HomeState>(
49 - listener: (context, state) {}, 50 + listener: (context, state) {
  51 + if (state is RequestVideoLessonState) {
  52 + final videoUrl = bloc.processEntity?.videos?.videoUrl??'';
  53 + var title = '';
  54 + if (state.type == 1) {
  55 + title = 'song';
  56 + }
  57 +
  58 + if (state.type == 2) {
  59 + title = 'video';
  60 + }
  61 +
  62 + if (state.type == 5) {
  63 + title = 'bonus';
  64 + }
  65 + debugPrint(videoUrl);
  66 + if (videoUrl.isEmpty) {
  67 + return;
  68 + }
  69 + Navigator.of(context).pushNamed(AppRouteName.lookVideo,arguments: {'videoUrl':videoUrl,'title':title});
  70 + }
  71 + },
50 child: _homeView(), 72 child: _homeView(),
51 ); 73 );
52 } 74 }
@@ -76,9 +98,10 @@ class _HomePageView extends StatelessWidget { @@ -76,9 +98,10 @@ class _HomePageView extends StatelessWidget {
76 return GestureDetector( 98 return GestureDetector(
77 onTap: () { 99 onTap: () {
78 if (data!.lock!) { 100 if (data!.lock!) {
  101 + showToast('当前课程暂未解锁');
79 return; 102 return;
80 } 103 }
81 - showToast('点击事件'); 104 + bloc.add(RequestVideoLessonEvent(data.id!,data.courseType!));
82 }, 105 },
83 child: HomeBoundsItem( 106 child: HomeBoundsItem(
84 imageUrl: data?.coverUrl, 107 imageUrl: data?.coverUrl,
@@ -87,17 +110,22 @@ class _HomePageView extends StatelessWidget { @@ -87,17 +110,22 @@ class _HomePageView extends StatelessWidget {
87 } else { 110 } else {
88 return GestureDetector( 111 return GestureDetector(
89 onTap: () { 112 onTap: () {
  113 + debugPrint('>>>>>>>类型${data?.courseType}');
90 if (data!.lock!) { 114 if (data!.lock!) {
  115 + showToast('当前课程暂未解锁');
91 return; 116 return;
92 } 117 }
93 - if (data!.courseType == 4) {//绘本 118 + if (data.courseType == 4) {//绘本
94 return; 119 return;
95 } 120 }
96 121
97 if (data.courseType == 3) {//练习 122 if (data.courseType == 3) {//练习
98 - Navigator.of(context).pushNamed(AppRouteName.topicPic,arguments: {'courseLessonId':data!.id}); 123 + Navigator.of(context).pushNamed(AppRouteName.topicPic,arguments: {'courseLessonId':data.id!});
99 return; 124 return;
100 } 125 }
  126 +
  127 + //儿歌/看视频
  128 + bloc.add(RequestVideoLessonEvent(data.id!,data.courseType!));
101 }, 129 },
102 child: HomeVideoItem( 130 child: HomeVideoItem(
103 lessons: data, 131 lessons: data,
lib/pages/home/widgets/home_tab_header_widget.dart
@@ -71,7 +71,7 @@ class HomeTabHeaderWidget extends StatelessWidget { @@ -71,7 +71,7 @@ class HomeTabHeaderWidget extends StatelessWidget {
71 20.horizontalSpace, 71 20.horizontalSpace,
72 const Expanded( 72 const Expanded(
73 child: Text( 73 child: Text(
74 - 'learn wow!yellow', 74 + 'learn wow',
75 textAlign: TextAlign.left, 75 textAlign: TextAlign.left,
76 style: TextStyle(color: Colors.white, fontSize: 30.0), 76 style: TextStyle(color: Colors.white, fontSize: 30.0),
77 )), 77 )),
lib/pages/practice/bloc/topic_picture_bloc.dart
@@ -8,10 +8,22 @@ import &#39;package:wow_english/common/request/dao/listen_dao.dart&#39;; @@ -8,10 +8,22 @@ import &#39;package:wow_english/common/request/dao/listen_dao.dart&#39;;
8 import 'package:wow_english/common/request/exception.dart'; 8 import 'package:wow_english/common/request/exception.dart';
9 import 'package:wow_english/models/course_process_entity.dart'; 9 import 'package:wow_english/models/course_process_entity.dart';
10 import 'package:wow_english/utils/loading.dart'; 10 import 'package:wow_english/utils/loading.dart';
  11 +import 'package:wow_english/utils/toast_util.dart';
11 12
12 part 'topic_picture_event.dart'; 13 part 'topic_picture_event.dart';
13 part 'topic_picture_state.dart'; 14 part 'topic_picture_state.dart';
14 15
  16 +enum VoicePlayState {
  17 + ///未知
  18 + unKnow,
  19 + ///播放中
  20 + playing,
  21 + ///播放完成
  22 + completed,
  23 + ///播放终止
  24 + stop
  25 +}
  26 +
15 class TopicPictureBloc extends Bloc<TopicPictureEvent, TopicPictureState> { 27 class TopicPictureBloc extends Bloc<TopicPictureEvent, TopicPictureState> {
16 28
17 final PageController pageController; 29 final PageController pageController;
@@ -27,6 +39,9 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; { @@ -27,6 +39,9 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
27 ///正在评测 39 ///正在评测
28 bool _isVoicing = false; 40 bool _isVoicing = false;
29 41
  42 + ///正在播放音频
  43 + VoicePlayState _voicePlayState = VoicePlayState.unKnow;
  44 +
30 CourseProcessEntity? get entity => _entity; 45 CourseProcessEntity? get entity => _entity;
31 46
32 int get currentPage => _currentPage + 1; 47 int get currentPage => _currentPage + 1;
@@ -35,29 +50,46 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; { @@ -35,29 +50,46 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
35 50
36 bool get isVoicing => _isVoicing; 51 bool get isVoicing => _isVoicing;
37 52
  53 + VoicePlayState get voicePlayState => _voicePlayState;
  54 +
38 late MethodChannel methodChannel; 55 late MethodChannel methodChannel;
39 56
40 late AudioPlayer audioPlayer; 57 late AudioPlayer audioPlayer;
41 58
42 TopicPictureBloc(this.pageController, this.courseLessonId) : super(TopicPictureInitial()) { 59 TopicPictureBloc(this.pageController, this.courseLessonId) : super(TopicPictureInitial()) {
43 on<CurrentPageIndexChangeEvent>(_pageControllerChange); 60 on<CurrentPageIndexChangeEvent>(_pageControllerChange);
  61 + on<VoicePlayStateChangeEvent>(_voicePlayStateChange);
  62 + on<XSVoiceResultEvent>(_voiceXsResult);
  63 + on<XSVoiceInitEvent>(_initVoiceSdk);
44 on<SelectItemEvent>(_selectItemLoad); 64 on<SelectItemEvent>(_selectItemLoad);
45 on<RequestDataEvent>(_requestData); 65 on<RequestDataEvent>(_requestData);
46 on<XSVoiceTestEvent>(_voiceXsTest); 66 on<XSVoiceTestEvent>(_voiceXsTest);
47 - on<XSVoiceResultEvent>(_voiceXsResult);  
48 - on<XSVoiceInitEvent>(_initVoiceSdk);  
49 - on<TopicPictureEvent>((event, emit) { 67 + on<XSVoiceStopEvent>(_voiceXsStop);
  68 + on<VoicePlayEvent>(_voicePlay);
  69 + on<InitBlocEvent>((event, emit) {
50 //音频播放器 70 //音频播放器
51 audioPlayer = AudioPlayer(); 71 audioPlayer = AudioPlayer();
52 - audioPlayer.onPlayerStateChanged.listen((event) { 72 + audioPlayer.onPlayerStateChanged.listen((event) async {
  73 + debugPrint('播放状态变化');
53 if (event == PlayerState.completed) { 74 if (event == PlayerState.completed) {
54 - if (kDebugMode) {  
55 - print('播放完成'); 75 + debugPrint('播放完成');
  76 + _voicePlayState = VoicePlayState.completed;
  77 + }
  78 + if (event == PlayerState.stopped) {
  79 + debugPrint('播放结束');
  80 + _voicePlayState = VoicePlayState.stop;
  81 + }
56 82
57 - }} 83 + if (event == PlayerState.playing) {
  84 + debugPrint('正在播放中');
  85 + _voicePlayState = VoicePlayState.playing;
  86 + }
  87 + if(isClosed) {
  88 + return;
  89 + }
  90 + add(VoicePlayStateChangeEvent());
58 }); 91 });
59 92
60 -  
61 methodChannel = const MethodChannel('wow_english/sing_sound_method_channely'); 93 methodChannel = const MethodChannel('wow_english/sing_sound_method_channely');
62 methodChannel.setMethodCallHandler((call) async { 94 methodChannel.setMethodCallHandler((call) async {
63 if (call.method == 'voiceResult') {//评测结果 95 if (call.method == 'voiceResult') {//评测结果
@@ -88,8 +120,9 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; { @@ -88,8 +120,9 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
88 } 120 }
89 121
90 @override 122 @override
91 - Future<void> close() { 123 + Future<void> close(){
92 pageController.dispose(); 124 pageController.dispose();
  125 + audioPlayer.release();
93 audioPlayer.dispose(); 126 audioPlayer.dispose();
94 return super.close(); 127 return super.close();
95 } 128 }
@@ -103,30 +136,26 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; { @@ -103,30 +136,26 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
103 }); 136 });
104 } catch (e) { 137 } catch (e) {
105 if (e is ApiException) { 138 if (e is ApiException) {
106 - EasyLoading.showToast(e.message??'请求失败,请检查网络连接'); 139 + showToast(e.message??'请求失败,请检查网络连接');
107 } 140 }
108 } 141 }
109 } 142 }
110 143
111 - ///初始化SDK  
112 - _initVoiceSdk(XSVoiceInitEvent event,Emitter<TopicPictureState> emitter) async {  
113 - methodChannel.invokeMethod('initVoiceSdk',event.data);  
114 - }  
115 -  
116 ///页面切换 144 ///页面切换
117 void _pageControllerChange(CurrentPageIndexChangeEvent event,Emitter<TopicPictureState> emitter) async { 145 void _pageControllerChange(CurrentPageIndexChangeEvent event,Emitter<TopicPictureState> emitter) async {
118 _currentPage = event.pageIndex; 146 _currentPage = event.pageIndex;
  147 + if (voicePlayState == VoicePlayState.playing) {
  148 + await audioPlayer.stop();
  149 + }
119 final topics = _entity?.topics?[_currentPage]; 150 final topics = _entity?.topics?[_currentPage];
120 - if (topics?.type == 1 || topics?.type == 2 || topics?.type == 5) {  
121 - audioPlayer.stop(); 151 + if (topics?.type != 3 && topics?.type != 4) {
122 if (topics?.audioUrl != null) { 152 if (topics?.audioUrl != null) {
123 final urlStr = topics?.audioUrl??''; 153 final urlStr = topics?.audioUrl??'';
124 if (urlStr.isNotEmpty) { 154 if (urlStr.isNotEmpty) {
125 - audioPlayer.play(UrlSource(urlStr)); 155 + debugPrint(urlStr);
  156 + await audioPlayer.play(UrlSource(urlStr));
126 } 157 }
127 } 158 }
128 - } else {  
129 - audioPlayer.stop();  
130 } 159 }
131 _selectItem = -1; 160 _selectItem = -1;
132 emitter(CurrentPageIndexState()); 161 emitter(CurrentPageIndexState());
@@ -135,12 +164,24 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; { @@ -135,12 +164,24 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
135 ///选择 164 ///选择
136 void _selectItemLoad(SelectItemEvent event,Emitter<TopicPictureState> emitter) async { 165 void _selectItemLoad(SelectItemEvent event,Emitter<TopicPictureState> emitter) async {
137 _selectItem = event.selectIndex; 166 _selectItem = event.selectIndex;
  167 + CourseProcessTopics? topics = _entity?.topics?[_currentPage];
  168 + CourseProcessTopicsTopicAnswerList? answerList = topics?.topicAnswerList?[_selectItem];
  169 + if (answerList?.correct == 0) {
  170 + showToast('继续加油哦',duration: const Duration(seconds: 2));
  171 + } else {
  172 + showToast('恭喜你,答对啦!',duration: const Duration(seconds: 2));
  173 + }
138 emitter(SelectItemChangeState()); 174 emitter(SelectItemChangeState());
139 } 175 }
140 176
  177 + ///初始化SDK
  178 + _initVoiceSdk(XSVoiceInitEvent event,Emitter<TopicPictureState> emitter) async {
  179 + methodChannel.invokeMethod('initVoiceSdk',event.data);
  180 + }
  181 +
141 ///先声测试 182 ///先声测试
142 void _voiceXsTest(XSVoiceTestEvent event,Emitter<TopicPictureState> emitter) async { 183 void _voiceXsTest(XSVoiceTestEvent event,Emitter<TopicPictureState> emitter) async {
143 - EasyLoading.show(status: '录音中....'); 184 + audioPlayer.stop();
144 methodChannel.invokeMethod( 185 methodChannel.invokeMethod(
145 'startVoice', 186 'startVoice',
146 {'word':event.testWord,'type':event.type,'userId':event.userId.toString()} 187 {'word':event.testWord,'type':event.type,'userId':event.userId.toString()}
@@ -149,16 +190,36 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; { @@ -149,16 +190,36 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
149 emitter(XSVoiceTestState()); 190 emitter(XSVoiceTestState());
150 } 191 }
151 192
  193 + ///终止评测
  194 + void _voiceXsStop(XSVoiceStopEvent event,Emitter<TopicPictureState> emitter) async {
  195 + methodChannel.invokeMethod('stopVoice');
  196 + }
  197 +
  198 + ///先声评测结果
152 void _voiceXsResult(XSVoiceResultEvent event,Emitter<TopicPictureState> emitter) async { 199 void _voiceXsResult(XSVoiceResultEvent event,Emitter<TopicPictureState> emitter) async {
153 final Map args = event.message as Map; 200 final Map args = event.message as Map;
154 final result = args['result'] as String; 201 final result = args['result'] as String;
155 if (result == '1') { 202 if (result == '1') {
156 final overall = args['overall'].toString(); 203 final overall = args['overall'].toString();
157 - EasyLoading.showToast('测评成功,分数是$overall',duration: const Duration(seconds: 10)); 204 + showToast('测评成功,分数是$overall',duration: const Duration(seconds: 5));
158 } else { 205 } else {
159 - EasyLoading.showToast('测评失败',duration: const Duration(seconds: 10)); 206 + showToast('测评失败',duration: const Duration(seconds: 5));
160 } 207 }
161 _isVoicing = false; 208 _isVoicing = false;
162 emitter(XSVoiceTestState()); 209 emitter(XSVoiceTestState());
163 } 210 }
  211 +
  212 + void _voicePlayStateChange(VoicePlayStateChangeEvent event,Emitter<TopicPictureState> emitter) async {
  213 + emitter(VoicePlayStateChange());
  214 + }
  215 +
  216 + void _voicePlay(VoicePlayEvent event,Emitter<TopicPictureState> emitter) async {
  217 + if (voicePlayState == VoicePlayState.playing) {
  218 + await audioPlayer.stop();
  219 + return;
  220 + }
  221 + final topics = _entity?.topics?[_currentPage];
  222 + final urlStr = topics?.audioUrl??'';
  223 + await audioPlayer.play(UrlSource(urlStr));
  224 + }
164 } 225 }
lib/pages/practice/bloc/topic_picture_event.dart
@@ -3,6 +3,8 @@ part of &#39;topic_picture_bloc.dart&#39;; @@ -3,6 +3,8 @@ part of &#39;topic_picture_bloc.dart&#39;;
3 @immutable 3 @immutable
4 abstract class TopicPictureEvent {} 4 abstract class TopicPictureEvent {}
5 5
  6 +class InitBlocEvent extends TopicPictureEvent {}
  7 +
6 class RequestDataEvent extends TopicPictureEvent {} 8 class RequestDataEvent extends TopicPictureEvent {}
7 9
8 ///初始化先声SDK 10 ///初始化先声SDK
@@ -11,13 +13,7 @@ class XSVoiceInitEvent extends TopicPictureEvent { @@ -11,13 +13,7 @@ class XSVoiceInitEvent extends TopicPictureEvent {
11 XSVoiceInitEvent(this.data); 13 XSVoiceInitEvent(this.data);
12 } 14 }
13 15
14 -///评测结果  
15 -class XSVoiceResultEvent extends TopicPictureEvent {  
16 - final dynamic message;  
17 - XSVoiceResultEvent(this.message);  
18 -}  
19 -  
20 -///先声测试 16 +///开始评测
21 class XSVoiceTestEvent extends TopicPictureEvent { 17 class XSVoiceTestEvent extends TopicPictureEvent {
22 final String testWord; 18 final String testWord;
23 final String type; 19 final String type;
@@ -25,12 +21,32 @@ class XSVoiceTestEvent extends TopicPictureEvent { @@ -25,12 +21,32 @@ class XSVoiceTestEvent extends TopicPictureEvent {
25 XSVoiceTestEvent(this.testWord,this.type,this.userId); 21 XSVoiceTestEvent(this.testWord,this.type,this.userId);
26 } 22 }
27 23
  24 +///终止评测
  25 +class XSVoiceStopEvent extends TopicPictureEvent {}
  26 +
  27 +///评测结果
  28 +class XSVoiceResultEvent extends TopicPictureEvent {
  29 + final dynamic message;
  30 + XSVoiceResultEvent(this.message);
  31 +}
  32 +
  33 +///音频播放状态变化
  34 +class VoicePlayStateChangeEvent extends TopicPictureEvent {}
  35 +
  36 +///页面切换
28 class CurrentPageIndexChangeEvent extends TopicPictureEvent { 37 class CurrentPageIndexChangeEvent extends TopicPictureEvent {
29 final int pageIndex; 38 final int pageIndex;
30 CurrentPageIndexChangeEvent(this.pageIndex); 39 CurrentPageIndexChangeEvent(this.pageIndex);
31 } 40 }
32 41
  42 +///选择答案
33 class SelectItemEvent extends TopicPictureEvent { 43 class SelectItemEvent extends TopicPictureEvent {
34 final int selectIndex; 44 final int selectIndex;
35 SelectItemEvent(this.selectIndex); 45 SelectItemEvent(this.selectIndex);
36 -}  
37 \ No newline at end of file 46 \ No newline at end of file
  47 +}
  48 +
  49 +///音频播放事件
  50 +class VoicePlayChangeEvent extends TopicPictureEvent {}
  51 +
  52 +///播放音乐
  53 +class VoicePlayEvent extends TopicPictureEvent {}
38 \ No newline at end of file 54 \ No newline at end of file
lib/pages/practice/bloc/topic_picture_state.dart
@@ -3,12 +3,14 @@ part of &#39;topic_picture_bloc.dart&#39;; @@ -3,12 +3,14 @@ part of &#39;topic_picture_bloc.dart&#39;;
3 @immutable 3 @immutable
4 abstract class TopicPictureState {} 4 abstract class TopicPictureState {}
5 5
  6 +class TopicPictureInitial extends TopicPictureState {}
  7 +
6 class RequestDataState extends TopicPictureState {} 8 class RequestDataState extends TopicPictureState {}
7 9
8 class XSVoiceTestState extends TopicPictureState {} 10 class XSVoiceTestState extends TopicPictureState {}
9 11
10 -class TopicPictureInitial extends TopicPictureState {}  
11 -  
12 class CurrentPageIndexState extends TopicPictureState {} 12 class CurrentPageIndexState extends TopicPictureState {}
13 13
14 class SelectItemChangeState extends TopicPictureState {} 14 class SelectItemChangeState extends TopicPictureState {}
  15 +
  16 +class VoicePlayStateChange extends TopicPictureState {}
lib/pages/practice/topic_picture_page.dart
@@ -5,11 +5,12 @@ import &#39;package:wow_english/common/core/user_util.dart&#39;; @@ -5,11 +5,12 @@ import &#39;package:wow_english/common/core/user_util.dart&#39;;
5 import 'package:wow_english/common/extension/string_extension.dart'; 5 import 'package:wow_english/common/extension/string_extension.dart';
6 import 'package:wow_english/common/widgets/ow_image_widget.dart'; 6 import 'package:wow_english/common/widgets/ow_image_widget.dart';
7 import 'package:wow_english/models/course_process_entity.dart'; 7 import 'package:wow_english/models/course_process_entity.dart';
  8 +import 'package:wow_english/utils/toast_util.dart';
8 9
9 import 'bloc/topic_picture_bloc.dart'; 10 import 'bloc/topic_picture_bloc.dart';
10 import 'widgets/practice_header_widget.dart'; 11 import 'widgets/practice_header_widget.dart';
11 12
12 -class TopicPicturePage extends StatelessWidget { 13 +class TopicPicturePage extends StatelessWidget {
13 const TopicPicturePage({super.key, this.courseLessonId}); 14 const TopicPicturePage({super.key, this.courseLessonId});
14 15
15 final String? courseLessonId; 16 final String? courseLessonId;
@@ -18,9 +19,10 @@ class TopicPicturePage extends StatelessWidget { @@ -18,9 +19,10 @@ class TopicPicturePage extends StatelessWidget {
18 Widget build(BuildContext context) { 19 Widget build(BuildContext context) {
19 return BlocProvider( 20 return BlocProvider(
20 create: (context) => TopicPictureBloc( 21 create: (context) => TopicPictureBloc(
21 - PageController(),  
22 - courseLessonId??'', 22 + PageController(),
  23 + courseLessonId??'',
23 ) 24 )
  25 + ..add(InitBlocEvent())
24 ..add(RequestDataEvent()) 26 ..add(RequestDataEvent())
25 ..add(XSVoiceInitEvent( 27 ..add(XSVoiceInitEvent(
26 { 28 {
@@ -258,13 +260,23 @@ class _TopicPicturePage extends StatelessWidget { @@ -258,13 +260,23 @@ class _TopicPicturePage extends StatelessWidget {
258 ///听音选图 260 ///听音选图
259 Widget _pageViewVoicePictureItemWidget(CourseProcessTopics? topics) => BlocBuilder<TopicPictureBloc,TopicPictureState>( 261 Widget _pageViewVoicePictureItemWidget(CourseProcessTopics? topics) => BlocBuilder<TopicPictureBloc,TopicPictureState>(
260 builder: (context, state){ 262 builder: (context, state){
  263 + final bloc = BlocProvider.of<TopicPictureBloc>(context);
261 return SafeArea( 264 return SafeArea(
262 child: Column( 265 child: Column(
263 children: [ 266 children: [
264 Row( 267 Row(
265 mainAxisAlignment: MainAxisAlignment.center, 268 mainAxisAlignment: MainAxisAlignment.center,
266 children: [ 269 children: [
267 - Image.asset('voice'.assetPng,height: 33.h,width: 30.w,), 270 + GestureDetector(
  271 + onTap: () {
  272 + bloc.add(VoicePlayEvent());
  273 + },
  274 + child: Image.asset(
  275 + bloc.voicePlayState == VoicePlayState.playing?'reade_answer'.assetGif:'voice'.assetPng,
  276 + height: 33.h,
  277 + width: 30.w,
  278 + ),
  279 + ),
268 10.horizontalSpace, 280 10.horizontalSpace,
269 Text( 281 Text(
270 topics?.word??'', 282 topics?.word??'',
@@ -331,10 +343,20 @@ class _TopicPicturePage extends StatelessWidget { @@ -331,10 +343,20 @@ class _TopicPicturePage extends StatelessWidget {
331 ///听音选字 343 ///听音选字
332 Widget _pageViewVoiceWordItemWidget(CourseProcessTopics? topics) => BlocBuilder<TopicPictureBloc,TopicPictureState>( 344 Widget _pageViewVoiceWordItemWidget(CourseProcessTopics? topics) => BlocBuilder<TopicPictureBloc,TopicPictureState>(
333 builder: (context, state){ 345 builder: (context, state){
  346 + final bloc = BlocProvider.of<TopicPictureBloc>(context);
334 return SafeArea( 347 return SafeArea(
335 child: Column( 348 child: Column(
336 children: [ 349 children: [
337 - Image.asset('voice'.assetPng,height: 33.h,width: 30.w,), 350 + GestureDetector(
  351 + onTap: () {
  352 + bloc.add(VoicePlayEvent());
  353 + },
  354 + child: Image.asset(
  355 + bloc.voicePlayState == VoicePlayState.playing?'reade_answer'.assetGif:'voice'.assetPng,
  356 + height: 33.h,
  357 + width: 30.w
  358 + )
  359 + ),
338 26.verticalSpace, 360 26.verticalSpace,
339 SizedBox( 361 SizedBox(
340 width: 163.w * (topics?.topicAnswerList?.length??0), 362 width: 163.w * (topics?.topicAnswerList?.length??0),
@@ -426,31 +448,45 @@ class _TopicPicturePage extends StatelessWidget { @@ -426,31 +448,45 @@ class _TopicPicturePage extends StatelessWidget {
426 Column( 448 Column(
427 mainAxisAlignment: MainAxisAlignment.center, 449 mainAxisAlignment: MainAxisAlignment.center,
428 children: [ 450 children: [
429 - Row(  
430 - children: [  
431 - Image.asset(  
432 - 'voice'.assetPng,  
433 - height: 52.h,  
434 - width: 46.w,  
435 - ),  
436 - 10.horizontalSpace,  
437 - Text(topics?.word??'')  
438 - ], 451 + GestureDetector(
  452 + onTap: () {
  453 + if (bloc.isVoicing) {
  454 + showToast('正在录音,不能终止');
  455 + return;
  456 + }
  457 + bloc.add(VoicePlayEvent());
  458 + },
  459 + child: Row(
  460 + children: [
  461 + Image.asset(
  462 + bloc.voicePlayState == VoicePlayState.playing?'reade_answer'.assetGif:'voice'.assetPng,
  463 + height: 52.h,
  464 + width: 46.w,
  465 + ),
  466 + 10.horizontalSpace,
  467 + Text(topics?.word??'')
  468 + ],
  469 + ),
439 ), 470 ),
440 70.verticalSpace, 471 70.verticalSpace,
441 GestureDetector( 472 GestureDetector(
442 onTap: () { 473 onTap: () {
  474 + if (bloc.voicePlayState == VoicePlayState.playing) {
  475 + showToast('正在播放音屏,不能终止');
  476 + return;
  477 + }
  478 +
443 if (bloc.isVoicing) { 479 if (bloc.isVoicing) {
444 return; 480 return;
445 } 481 }
446 - if (topics?.type == 5) { 482 + if (topics?.type == 5 || topics?.type == 7) {
447 bloc.add(XSVoiceTestEvent(topics?.keyWord??'', '0',UserUtil.getUser()!.id.toString())); 483 bloc.add(XSVoiceTestEvent(topics?.keyWord??'', '0',UserUtil.getUser()!.id.toString()));
448 } else { 484 } else {
449 bloc.add(XSVoiceTestEvent(topics?.word??'', '0',UserUtil.getUser()!.id.toString())); 485 bloc.add(XSVoiceTestEvent(topics?.word??'', '0',UserUtil.getUser()!.id.toString()));
450 } 486 }
451 }, 487 },
452 child: Image.asset( 488 child: Image.asset(
453 - 'micro_phone'.assetPng, 489 + bloc.isVoicing?'micro_phone'.assetGif:'micro_phone'.assetPng,
454 height: 75.w, 490 height: 75.w,
455 width: 75.w, 491 width: 75.w,
456 ), 492 ),
lib/pages/reading/bloc/reading_bloc.dart
@@ -125,7 +125,7 @@ class ReadingPageBloc extends Bloc&lt;ReadingPageEvent, ReadingPageState&gt; { @@ -125,7 +125,7 @@ class ReadingPageBloc extends Bloc&lt;ReadingPageEvent, ReadingPageState&gt; {
125 if (readingData?.audioUrl != null) { 125 if (readingData?.audioUrl != null) {
126 final urlStr = audioUrl ?? readingData?.audioUrl ?? ''; 126 final urlStr = audioUrl ?? readingData?.audioUrl ?? '';
127 if (urlStr.isNotEmpty) { 127 if (urlStr.isNotEmpty) {
128 - audioPlayer.play(UrlSource(urlStr)); 128 + audioPlayer.play(UrlSource(urlStr));
129 } 129 }
130 } 130 }
131 } 131 }
lib/pages/video/lookvideo/widgets/video_opera_widget.dart
@@ -94,6 +94,7 @@ class _VideoOperaWidgetState extends State&lt;VideoOperaWidget&gt; { @@ -94,6 +94,7 @@ class _VideoOperaWidgetState extends State&lt;VideoOperaWidget&gt; {
94 child: Text( 94 child: Text(
95 widget.title, 95 widget.title,
96 textAlign: TextAlign.center, 96 textAlign: TextAlign.center,
  97 + overflow: TextOverflow.clip,
97 style: TextStyle( 98 style: TextStyle(
98 fontSize: 20.sp, 99 fontSize: 20.sp,
99 color: const Color(0xFF333333), 100 color: const Color(0xFF333333),