Commit 22f362320722bc706da82d9fd40629dde0345b76

Authored by 吴启风
1 parent aa0d2360

feat:过渡页-练习环节

lib/pages/practice/bloc/topic_picture_bloc.dart
... ... @@ -9,10 +9,14 @@ import 'package:wow_english/common/extension/string_extension.dart';
9 9 import 'package:wow_english/common/request/dao/listen_dao.dart';
10 10 import 'package:wow_english/common/request/exception.dart';
11 11 import 'package:wow_english/models/course_process_entity.dart';
  12 +import 'package:wow_english/pages/section/subsection/base_section/bloc.dart';
  13 +import 'package:wow_english/pages/section/subsection/base_section/event.dart';
  14 +import 'package:wow_english/pages/section/subsection/base_section/state.dart';
12 15 import 'package:wow_english/utils/loading.dart';
13 16 import 'package:wow_english/utils/toast_util.dart';
14 17  
15 18 import '../../../common/permission/permissionRequestPage.dart';
  19 +import '../../../route/route.dart';
16 20  
17 21 part 'topic_picture_event.dart';
18 22 part 'topic_picture_state.dart';
... ... @@ -28,7 +32,7 @@ enum VoicePlayState {
28 32 stop
29 33 }
30 34  
31   -class TopicPictureBloc extends Bloc<TopicPictureEvent, TopicPictureState> {
  35 +class TopicPictureBloc extends BaseSectionBloc<TopicPictureEvent, TopicPictureState> {
32 36  
33 37 final PageController pageController;
34 38  
... ... @@ -94,11 +98,15 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
94 98 _forbiddenWhenCorrect = false;
95 99 debugPrint('播放完成后解除禁止');
96 100 if (event == PlayerState.completed) {
97   - // 答对后且播放完自动翻页
98   - pageController.nextPage(
99   - duration: const Duration(milliseconds: 500),
100   - curve: Curves.ease,
101   - );
  101 + if (isLastPage()) {
  102 + showStepPage();
  103 + } else {
  104 + // 答对后且播放完自动翻页
  105 + pageController.nextPage(
  106 + duration: const Duration(milliseconds: 500),
  107 + curve: Curves.ease,
  108 + );
  109 + }
102 110 }
103 111 }
104 112 }
... ... @@ -263,6 +271,9 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
263 271 showToast('测评成功,分数是$overall',duration: const Duration(seconds: 5));
264 272 _isVoicing = false;
265 273 emitter(XSVoiceTestState());
  274 + if (isLastPage()) {
  275 + showStepPage();
  276 + }
266 277 }
267 278  
268 279 // 暂时没用上
... ... @@ -288,6 +299,7 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
288 299 }
289 300 }
290 301  
  302 + ///播放选择结果音效
291 303 void _playResultSound(bool isCorrect) async {
292 304 // await audioPlayer.stop();
293 305 if (audioPlayer.state == PlayerState.playing && _isResultSoundPlaying == false) {
... ... @@ -302,4 +314,26 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
302 314 await audioPlayer.play(AssetSource('incorrect_voice'.assetMp3));
303 315 }
304 316 }
  317 +
  318 + ///是否是最后一页
  319 + bool isLastPage() {
  320 + return currentPage == _entity?.topics?.length;
  321 + }
  322 +
  323 + ///展示过渡页
  324 + void showStepPage() {
  325 + ///如果最后一页是语音问答题,评测完后自动翻页
  326 + sectionComplete(() {
  327 + popPage(
  328 + data:{
  329 + 'currentStep':currentPage.toString(),
  330 + 'courseLessonId':courseLessonId,
  331 + 'isLastPage': true,
  332 + 'nextSection': true
  333 + });
  334 + }, againSectionTap: () {
  335 + debugPrint("WQF 重做");
  336 + pageController.jumpToPage(0);
  337 + });
  338 + }
305 339 }
... ...
lib/pages/practice/bloc/topic_picture_event.dart
1 1 part of 'topic_picture_bloc.dart';
2 2  
3 3 @immutable
4   -abstract class TopicPictureEvent {}
  4 +abstract class TopicPictureEvent extends BaseSectionEvent {}
5 5  
6 6 class InitBlocEvent extends TopicPictureEvent {}
7 7  
... ...
lib/pages/practice/bloc/topic_picture_state.dart
1 1 part of 'topic_picture_bloc.dart';
2 2  
3 3 @immutable
4   -abstract class TopicPictureState {}
  4 +abstract class TopicPictureState extends BaseSectionState {}
5 5  
6 6 class TopicPictureInitial extends TopicPictureState {}
7 7  
... ...
lib/pages/practice/topic_picture_page.dart
... ... @@ -6,6 +6,7 @@ import &#39;package:wow_english/common/core/user_util.dart&#39;;
6 6 import 'package:wow_english/common/extension/string_extension.dart';
7 7 import 'package:wow_english/common/widgets/ow_image_widget.dart';
8 8 import 'package:wow_english/models/course_process_entity.dart';
  9 +import 'package:wow_english/pages/practice/topic_type.dart';
9 10 import 'package:wow_english/route/route.dart';
10 11 import 'package:wow_english/utils/toast_util.dart';
11 12  
... ... @@ -71,7 +72,8 @@ class _TopicPicturePage extends StatelessWidget {
71 72 popPage(
72 73 data:{
73 74 'currentStep':bloc.currentPage.toString(),
74   - 'courseLessonId':bloc.courseLessonId
  75 + 'courseLessonId':bloc.courseLessonId,
  76 + 'isLastPage': bloc.isLastPage(),
75 77 });
76 78 // Navigator.pop(context);
77 79 },
... ... @@ -86,13 +88,13 @@ class _TopicPicturePage extends StatelessWidget {
86 88 },
87 89 itemBuilder: (BuildContext context,int index){
88 90 CourseProcessTopics? topics = bloc.entity?.topics![index];
89   - if (topics?.type == 1) {//听音选图
  91 + if (topics?.type == TopicType.audioImageSelect.value) {//听音选图
90 92 return _pageViewVoicePictureItemWidget(topics);
91   - } else if (topics?.type == 2) {//听音选字
  93 + } else if (topics?.type == TopicType.audioCharSelect.value) {//听音选字
92 94 return _pageViewVoiceWordItemWidget(topics);
93   - } else if (topics?.type == 3) {//看题选字
  95 + } else if (topics?.type == TopicType.questionCharSelect.value) {//看题选字
94 96 return _pageViewWordItemWidget(topics);
95   - } else if (topics?.type == 4) {//看题选图
  97 + } else if (topics?.type == TopicType.questionImageSelect.value) {//看题选图
96 98 return _pageViewItemWidget(topics);
97 99 } else {//语音问答
98 100 return _voiceAnswerItem(topics);
... ...
lib/pages/practice/topic_type.dart 0 → 100644
  1 +///练习(题型)类型
  2 +enum TopicType {
  3 + ///听音选图
  4 + audioImageSelect,
  5 +
  6 + ///听音选字
  7 + audioCharSelect,
  8 +
  9 + ///看题选字
  10 + questionCharSelect,
  11 +
  12 + ///看题选图
  13 + questionImageSelect,
  14 +
  15 + ///语音问答
  16 + voiceQuestion
  17 +}
  18 +
  19 +extension TopicTypeExtension on TopicType {
  20 + int get value {
  21 + switch (this) {
  22 + case TopicType.audioImageSelect:
  23 + return 1;
  24 + case TopicType.audioCharSelect:
  25 + return 2;
  26 + case TopicType.questionCharSelect:
  27 + return 3;
  28 + case TopicType.questionImageSelect:
  29 + return 4;
  30 + case TopicType.voiceQuestion:
  31 + return 5;
  32 + default:
  33 + throw ArgumentError('Unknown topic type');
  34 + }
  35 + }
  36 +}
... ...
lib/pages/section/bloc/section_bloc.dart
... ... @@ -46,19 +46,22 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
46 46  
47 47 CourseProcessEntity? get processEntity => _processEntity;
48 48  
49   - SectionBloc(this._courseUnitEntity, this._courseUnitDetail, this._pageController, this._listController) : super(LessonInitial()) {
  49 + SectionBloc(this._courseUnitEntity, this._courseUnitDetail,
  50 + this._pageController, this._listController)
  51 + : super(LessonInitial()) {
50 52 on<RequestDataEvent>(_requestData);
51   - on<RequestExitClassEvent>(_requestExitClass);
52 53 on<RequestEndClassEvent>(_requestEndClass);
53 54 on<RequestEnterClassEvent>(_requestEnterClass);
54 55 on<RequestVideoLessonEvent>(_requestVideoLesson);
55 56 on<CurrentUnitIndexChangeEvent>(_pageControllerChange);
56 57 }
57 58  
58   - void _requestData(RequestDataEvent event, Emitter<SectionState> emitter) async {
  59 + void _requestData(
  60 + RequestDataEvent event, Emitter<SectionState> emitter) async {
59 61 try {
60 62 await loading(() async {
61   - _courseSectionDatas = await LessonDao.courseSection(courseUnitId: _courseUnitDetail.id!);
  63 + _courseSectionDatas =
  64 + await LessonDao.courseSection(courseUnitId: _courseUnitDetail.id!);
62 65 emitter(LessonDataLoadState());
63 66 });
64 67 } catch (e) {
... ... @@ -68,68 +71,84 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
68 71 }
69 72 }
70 73  
71   - void _requestVideoLesson(RequestVideoLessonEvent event, Emitter<SectionState> emitter) async {
  74 + void _requestVideoLesson(
  75 + RequestVideoLessonEvent event, Emitter<SectionState> emitter) async {
72 76 try {
73 77 await loading(() async {
74 78 _processEntity = await ListenDao.process(event.courseLessonId);
75   - emitter(RequestVideoLessonState(event.courseLessonId,event.courseType));
  79 + emitter(
  80 + RequestVideoLessonState(event.courseLessonId, event.courseType));
76 81 });
77 82 } catch (e) {
78 83 if (e is ApiException) {
79   - showToast(e.message??'请求失败,请检查网络连接');
  84 + showToast(e.message ?? '请求失败,请检查网络连接');
80 85 }
81 86 }
82 87 }
83 88  
84   -
85   - void _requestEnterClass(RequestEnterClassEvent event,Emitter<SectionState> emitter) async {
  89 + void _requestEnterClass(
  90 + RequestEnterClassEvent event, Emitter<SectionState> emitter) async {
86 91 try {
87 92 await loading(() async {
88   - await ListenDao.enterClass(event.courseLessonId);
89   - emitter(RequestEnterClassState(event.courseLessonId,event.courseType));
  93 + await ListenDao.enterClass(event.courseLessonId);
  94 + emitter(RequestEnterClassState(event.courseLessonId, event.courseType));
90 95 });
91 96 } catch (e) {
92 97 if (e is ApiException) {
93   - showToast(e.message??'请求失败,请检查网络连接');
  98 + showToast(e.message ?? '请求失败,请检查网络连接');
94 99 }
95 100 }
96 101 }
97 102  
98   - void _requestExitClass(RequestExitClassEvent event,Emitter<SectionState> emitter) async {
99   - await ListenDao.exitClass(event.courseLessonId,event.currentStep);
100   - }
101   -
102   - void _requestEndClass(RequestEndClassEvent event,Emitter<SectionState> emitter) async {
103   - final obj = await ListenDao.endClass(event.courseLessonId,event.currentStep);
  103 + void _requestEndClass(
  104 + RequestEndClassEvent event, Emitter<SectionState> emitter) async {
  105 + if (event.isLastPage) {
  106 + await await ListenDao.endClass(event.courseLessonId, event.currentStep,
  107 + currentTime: event.currentTime);
  108 + } else {
  109 + await await ListenDao.exitClass(event.courseLessonId, event.currentStep,
  110 + currentTime: event.currentTime);
  111 + }
  112 + debugPrint("WQF _requestEndClass autoNextSection=${event.autoNextSection}");
104 113 if (event.autoNextSection) {
105   - final nextCourseSection = getNextCourseSectionBySort(int.parse(event.courseLessonId));
  114 + final nextCourseSection =
  115 + getNextCourseSectionBySort(int.parse(event.courseLessonId));
  116 + debugPrint("WQF nextCourseSection = $nextCourseSection");
106 117 ///进入课堂
107   - add(RequestEnterClassEvent(nextCourseSection!.id.toString() ?? '', nextCourseSection.courseType));
  118 + add(RequestEnterClassEvent(nextCourseSection!.id.toString(),
  119 + nextCourseSection.courseType));
108 120 }
109 121 }
110 122  
111   - void _pageControllerChange(CurrentUnitIndexChangeEvent event,
112   - Emitter<SectionState> emitter) async {
  123 + void _pageControllerChange(
  124 + CurrentUnitIndexChangeEvent event, Emitter<SectionState> emitter) async {
113 125 _currentPage = event.unitIndex;
114 126 emitter(CurrentPageIndexState());
115 127 }
116 128  
117 129 // 未锁定的页数
118 130 int unlockPageCount() {
119   - return _courseUnitEntity.courseUnitVOList?.indexWhereOrNull((element) => element.lock == true) ?? 1;
  131 + return _courseUnitEntity.courseUnitVOList
  132 + ?.indexWhereOrNull((element) => element.lock == true) ??
  133 + 1;
120 134 }
121 135  
122 136 CourseSectionEntity? getNextCourseSectionBySort(int courseLessonId) {
123   - final curCourseSectionEntity = _courseSectionDatas?.firstWhere((element) => element.id == courseLessonId);
  137 + final curCourseSectionEntity = _courseSectionDatas
  138 + ?.firstWhere((element) => element.id == courseLessonId);
124 139 final curSort = curCourseSectionEntity?.sortOrder ?? 0;
125   - CourseSectionEntity? nextCourseSectionEntity = _courseSectionDatas?.firstWhere((element) => element.sortOrder == curSort + 1);
  140 + CourseSectionEntity? nextCourseSectionEntity = _courseSectionDatas
  141 + ?.firstWhere((element) => element.sortOrder == curSort + 1);
126 142 if (nextCourseSectionEntity != null) {
127 143 return nextCourseSectionEntity;
128 144 } else {
129 145 ///跨unit选lesson
130   - final curCourseUnitDetail = _courseUnitEntity.courseUnitVOList?.firstWhere((element) => element.id == curCourseSectionEntity?.courseUnitId);
  146 + final curCourseUnitDetail = _courseUnitEntity.courseUnitVOList
  147 + ?.firstWhere(
  148 + (element) => element.id == curCourseSectionEntity?.courseUnitId);
131 149 if (curCourseUnitDetail != null) {
132   - final nextCourseUnitDetail = _courseUnitEntity.courseUnitVOList?.firstWhere((element) => element.sortOrder == 0);
  150 + final nextCourseUnitDetail = _courseUnitEntity.courseUnitVOList
  151 + ?.firstWhere((element) => element.sortOrder == 0);
133 152 if (nextCourseUnitDetail != null) {
134 153 ///pageView翻页了,可能需要预加载 todo
135 154 return null;
... ...
lib/pages/section/bloc/section_event.dart
... ... @@ -9,6 +9,7 @@ class RequestDataEvent extends SectionEvent {}
9 9 class RequestVideoLessonEvent extends SectionEvent {
10 10 final String courseLessonId;
11 11 final int courseType;
  12 +
12 13 RequestVideoLessonEvent(this.courseLessonId, this.courseType);
13 14 }
14 15  
... ... @@ -16,29 +17,29 @@ class RequestVideoLessonEvent extends SectionEvent {
16 17 class RequestEnterClassEvent extends SectionEvent {
17 18 final String courseLessonId;
18 19 final int courseType;
19   - RequestEnterClassEvent(this.courseLessonId,this.courseType);
20   -}
21 20  
22   -///退出课堂
23   -class RequestExitClassEvent extends SectionEvent {
24   - final String courseLessonId;
25   - final String currentStep;
26   - final String currentTime;
27   - RequestExitClassEvent(this.courseLessonId,this.currentStep,this.currentTime);
  21 + RequestEnterClassEvent(this.courseLessonId, this.courseType);
28 22 }
29 23  
30 24 ///结束课堂
31 25 class RequestEndClassEvent extends SectionEvent {
32 26 final String courseLessonId;
33 27 final String currentStep;
34   - final String currentTime;
  28 +
  29 + ///是否是最后一页(决定调结束接口还是退出接口)
  30 + final bool isLastPage;
  31 + final int? currentTime;
  32 +
35 33 ///自动进入下一环节
36 34 final bool autoNextSection;
37   - RequestEndClassEvent(this.courseLessonId,this.currentStep,this.currentTime,{this.autoNextSection = false});
  35 +
  36 + RequestEndClassEvent(this.courseLessonId, this.currentStep, this.isLastPage,
  37 + {this.currentTime, this.autoNextSection = false});
38 38 }
39 39  
40 40 ///页面切换
41 41 class CurrentUnitIndexChangeEvent extends SectionEvent {
42 42 final int unitIndex;
  43 +
43 44 CurrentUnitIndexChangeEvent(this.unitIndex);
44 45 }
... ...
lib/pages/section/section_page.dart
... ... @@ -76,8 +76,8 @@ class _SectionPageView extends StatelessWidget {
76 76 }).then((value) {
77 77 if (value != null) {
78 78 Map<String, dynamic> dataMap = value as Map<String, dynamic>;
79   - bloc.add(RequestEndClassEvent(dataMap['courseLessonId']!, '0',
80   - dataMap['currentTime']!, autoNextSection: dataMap['nextSection'] as bool));
  79 + bloc.add(RequestEndClassEvent(dataMap['courseLessonId']!,
  80 + dataMap['currentTime']!, true, autoNextSection: dataMap['nextSection'] as bool));
81 81 }
82 82 });
83 83 return;
... ... @@ -99,9 +99,10 @@ class _SectionPageView extends StatelessWidget {
99 99 arguments: {'courseLessonId': state.courseLessonId})
100 100 .then((value) {
101 101 if (value != null) {
102   - Map<String, String> dataMap = value as Map<String, String>;
103   - bloc.add(RequestExitClassEvent(
104   - dataMap['courseLessonId']!, dataMap['currentStep']!, '0'));
  102 + Map<String, dynamic> dataMap = value as Map<String, dynamic>;
  103 + bloc.add(RequestEndClassEvent(
  104 + dataMap['courseLessonId']!, dataMap['currentStep']!,
  105 + dataMap['isLastPage']! as bool, autoNextSection: dataMap['nextSection'] as bool));
105 106 }
106 107 });
107 108 return;
... ... @@ -113,9 +114,10 @@ class _SectionPageView extends StatelessWidget {
113 114 arguments: {'courseLessonId': state.courseLessonId})
114 115 .then((value) {
115 116 if (value != null) {
116   - Map<String, String> dataMap = value as Map<String, String>;
117   - bloc.add(RequestExitClassEvent(
118   - dataMap['courseLessonId']!, dataMap['currentStep']!, '0'));
  117 + Map<String, dynamic> dataMap = value as Map<String, dynamic>;
  118 + bloc.add(RequestEndClassEvent(
  119 + dataMap['courseLessonId']!, dataMap['currentStep']!,
  120 + dataMap['isLastPage']! as bool, autoNextSection: dataMap['nextSection'] as bool));
119 121 }
120 122 });
121 123 return;
... ...
lib/pages/section/subsection/base_section/bloc.dart
... ... @@ -14,7 +14,7 @@ abstract class BaseSectionBloc&lt;E extends BaseSectionEvent,
14 14  
15 15 ///这里可以定义一些通用的逻辑
16 16 void sectionComplete(final VoidCallback? nextSectionTap,
17   - {BuildContext? context}) {
  17 + {VoidCallback? againSectionTap, BuildContext? context}) {
18 18 // 逻辑来标记步骤为已完成
19 19 // 比如更新状态
20 20 if (isCompleteDialogShow) {
... ... @@ -38,8 +38,8 @@ abstract class BaseSectionBloc&lt;E extends BaseSectionEvent,
38 38 flex: 1,
39 39 child: GestureDetector(
40 40 onTap: () {
41   - add(SectionAgainEvent() as E);
42 41 popPage();
  42 + againSectionTap!();
43 43 },
44 44 child: Image.asset('section_finish_again'.assetPng),
45 45 ),
... ...