Commit 2b3842c55ad6f089f824572e5c916195bc07e4a0

Authored by xiaoyu
2 parents 93825fa5 9e085b2a

Merge branch 'feat-wqf-payment' into ios_umeng

.gitignore
... ... @@ -68,6 +68,7 @@
68 68 **/ios/xcode_build_ipa_adh
69 69 **/ios/xcode_build_ipa_aps
70 70 **/ios/xcode_build_ipa_dev
  71 +**/ios/build/
71 72  
72 73 # Exceptions to above rules.
73 74 !**/ios/**/default.mode1v3
... ...
lib/pages/home/bloc.dart
... ... @@ -16,7 +16,6 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
16 16  
17 17 void _init(InitEvent event, Emitter<HomeState> emit) async {
18 18 await _checkUpdate(emit);
19   - debugPrint('WQF ModuleSelectBloc _init');
20 19 }
21 20  
22 21 Future<void> _checkUpdate(Emitter<HomeState> emit) async {
... ...
lib/pages/home/view.dart
... ... @@ -13,7 +13,6 @@ import &#39;package:wow_english/pages/user/bloc/user_bloc.dart&#39;;
13 13  
14 14 import '../../common/core/user_util.dart';
15 15 import '../../common/dialogs/show_dialog.dart';
16   -import '../../utils/log_util.dart';
17 16 import 'bloc.dart';
18 17 import 'event.dart';
19 18 import 'package:flutter_screenutil/flutter_screenutil.dart';
... ... @@ -38,11 +37,9 @@ class _HomePageView extends StatelessWidget {
38 37 Widget build(BuildContext context) {
39 38 return MultiBlocListener(listeners: [
40 39 BlocListener<UserBloc, UserState>(listener: (context, state) {
41   - debugPrint('WQF ModuleSelectPage BlocListener state: $state');
42 40 }),
43 41 BlocListener<HomeBloc, HomeState>(
44 42 listener: (context, state) {
45   - Log.d("WQF HomePage listener state: $state");
46 43 if (state is UpdateDialogState) {
47 44 _showUpdateDialog(context, state.forceUpdate, state.appVersionEntity);
48 45 }
... ... @@ -97,8 +94,6 @@ class _HomePageView extends StatelessWidget {
97 94 Expanded(
98 95 child: BlocBuilder<UserBloc, UserState>(
99 96 builder: (context, userState) {
100   - debugPrint(
101   - 'WQF ModuleSelectPage BlocBuilder state: $userState');
102 97 return GestureDetector(
103 98 onTap: () {
104 99 _checkPermission(() {
... ...
lib/pages/practice/bloc/topic_picture_bloc.dart
... ... @@ -103,7 +103,7 @@ class TopicPictureBloc extends BaseSectionBloc&lt;TopicPictureEvent, TopicPictureSt
103 103 } else {
104 104 // 答对后且播放完自动翻页
105 105 pageController.nextPage(
106   - duration: const Duration(milliseconds: 500),
  106 + duration: const Duration(milliseconds: 250),
107 107 curve: Curves.ease,
108 108 );
109 109 }
... ... @@ -332,7 +332,6 @@ class TopicPictureBloc extends BaseSectionBloc&lt;TopicPictureEvent, TopicPictureSt
332 332 'nextSection': true
333 333 });
334 334 }, againSectionTap: () {
335   - debugPrint("WQF 重做");
336 335 pageController.jumpToPage(0);
337 336 });
338 337 }
... ...
lib/pages/reading/bloc/reading_bloc.dart
... ... @@ -287,7 +287,7 @@ class ReadingPageBloc
287 287 } else {
288 288 _currentPage += 1;
289 289 pageController.nextPage(
290   - duration: const Duration(milliseconds: 500),
  290 + duration: const Duration(milliseconds: 250),
291 291 curve: Curves.ease,
292 292 );
293 293 }
... ...
lib/pages/section/bloc/section_bloc.dart
1 1 import 'package:flutter/cupertino.dart';
2 2 import 'package:flutter/foundation.dart';
  3 +import 'package:flutter/material.dart';
3 4 import 'package:flutter_bloc/flutter_bloc.dart';
  5 +import 'package:flutter_screenutil/flutter_screenutil.dart';
4 6 import 'package:wow_english/common/request/dao/lesson_dao.dart';
5 7 import 'package:wow_english/common/request/exception.dart';
6 8 import 'package:wow_english/common/request/dao/listen_dao.dart';
... ... @@ -13,10 +15,10 @@ import &#39;../../../models/course_unit_entity.dart&#39;;
13 15 import '../../../utils/list_ext.dart';
14 16  
15 17 part 'section_event.dart';
  18 +
16 19 part 'section_state.dart';
17 20  
18 21 class SectionBloc extends Bloc<SectionEvent, SectionState> {
19   -
20 22 PageController _pageController;
21 23  
22 24 PageController get pageController => _pageController;
... ... @@ -30,10 +32,17 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
30 32  
31 33 ScrollController get listController => _listController;
32 34  
  35 + ScrollController _indicatorSrollController;
  36 +
  37 + ScrollController get indicatorSrollController => _indicatorSrollController;
  38 +
33 39 CourseUnitEntity _courseUnitEntity;
34 40  
35 41 CourseUnitEntity get courseUnitEntity => _courseUnitEntity;
36 42  
  43 + ///单元列表是否有刷新,有的话返回上一页时通知其刷新接口数据
  44 + bool courseUnitEntityChanged = false;
  45 +
37 46 ///courseUnitId与课程环节列表的映射
38 47 final Map<int, List<CourseSectionEntity>?> _courseSectionDatasMap = {};
39 48  
... ... @@ -45,7 +54,7 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
45 54 CourseProcessEntity? get processEntity => _processEntity;
46 55  
47 56 SectionBloc(this._courseUnitEntity, this._currentPage, this._pageController,
48   - this._listController)
  57 + this._listController, this._indicatorSrollController)
49 58 : super(LessonInitial()) {
50 59 on<RequestDataEvent>(_requestSectionsData);
51 60 on<RequestEndClassEvent>(_requestEndClass);
... ... @@ -121,15 +130,38 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
121 130 void _pageControllerChange(
122 131 CurrentUnitIndexChangeEvent event, Emitter<SectionState> emitter) async {
123 132 _currentPage = event.unitIndex;
  133 + double indicatorWidth = 30.0.w; // 指示器宽度
  134 +
  135 + // 计算选中项的偏移量
  136 + double offset = _currentPage * indicatorWidth -
  137 + (_indicatorSrollController.position.viewportDimension -
  138 + indicatorWidth) /
  139 + 2;
  140 +
  141 + // 确保偏移量在合理范围内
  142 + if (offset < 0) {
  143 + offset = 0;
  144 + } else if (offset >
  145 + unlockPageCount() * indicatorWidth -
  146 + _indicatorSrollController.position.viewportDimension) {
  147 + offset = unlockPageCount() * indicatorWidth -
  148 + _indicatorSrollController.position.viewportDimension;
  149 + }
  150 + _indicatorSrollController.animateTo(
  151 + offset,
  152 + duration: const Duration(milliseconds: 250),
  153 + curve: Curves.easeInOut,
  154 + );
124 155 emitter(CurrentPageIndexState());
125 156 }
126 157  
127 158 ///未锁定的页(单元)数
128 159 int unlockPageCount() {
129   - return _courseUnitEntity.courseUnitVOList
130   - ?.indexWhereOrNull((element) => element.lock == true) ??
131   - _courseUnitEntity.courseUnitVOList?.length ??
132   - 0;
  160 + // return _courseUnitEntity.courseUnitVOList
  161 + // ?.indexWhereOrNull((element) => element.lock == true) ??
  162 + // _courseUnitEntity.courseUnitVOList?.length ??
  163 + // 0;
  164 + return _courseUnitEntity.courseUnitVOList?.length ?? 0;
133 165 }
134 166  
135 167 ///当前页的课程详情
... ... @@ -172,7 +204,7 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
172 204 final curCourseSectionEntity = findCourseSectionById(courseLessonId);
173 205 if (curCourseSectionEntity != null) {
174 206 final curCourseUnitDetail = _courseUnitEntity.courseUnitVOList
175   - ?.firstWhere(
  207 + ?.firstWhereOrNull(
176 208 (element) => element.id == curCourseSectionEntity.courseUnitId);
177 209 return curCourseUnitDetail;
178 210 }
... ... @@ -185,39 +217,89 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
185 217 final curCourseSectionEntity = findCourseSectionById(courseLessonId);
186 218 final curSectionSort = curCourseSectionEntity?.sortOrder ?? 0;
187 219  
188   - ///查找下一个section
189   - final nextCourseSectionEntity = findCourseSectionBySort(curSectionSort + 1);
190   - if (nextCourseSectionEntity != null) {
191   - return nextCourseSectionEntity;
  220 + try {
  221 + ///查找当前unit的下一个section
  222 + CourseSectionEntity? nextCourseSectionEntity =
  223 + findCourseSectionBySort(curSectionSort + 1);
  224 + return checkCourseSectionLocked(courseLessonId, nextCourseSectionEntity, emitter);
  225 + } catch (e) {
  226 + if (e is ApiException) {
  227 + showToast(e.message.toString());
  228 + }
  229 + return null;
  230 + }
  231 + }
  232 +
  233 + ///检查section是否锁定
  234 + Future<CourseSectionEntity?> checkCourseSectionLocked(int courseLessonId, CourseSectionEntity? courseSectionEntity,
  235 + Emitter<SectionState> emitter) async {
  236 + if (courseSectionEntity != null) {
  237 + if (courseSectionEntity.lock == false) {
  238 + ///如果section没锁,直接返回
  239 + return courseSectionEntity;
  240 + } else {
  241 + ///如果section锁了,请求当前unit下的section数据,查询解锁状态
  242 + int courseUnitId = courseSectionEntity.courseUnitId;
  243 + CourseSectionEntity? result = await loading(() async {
  244 + List<CourseSectionEntity>? tempSectionEntities =
  245 + await LessonDao.courseSection(courseUnitId: courseUnitId);
  246 + if (tempSectionEntities != null) {
  247 + _courseSectionDatasMap[courseUnitId] = tempSectionEntities;
  248 + emitter(LessonDataLoadState());
  249 + }
  250 + courseSectionEntity = tempSectionEntities?.firstWhereOrNull(
  251 + (element) => element.id == courseSectionEntity?.id);
  252 + if (courseSectionEntity?.lock == false) {
  253 + ///刷新后的数据如果解锁了,直接返回
  254 + return courseSectionEntity;
  255 + } else {
  256 + ///请求失败或者锁定状态没变(没变就感觉状态异常了,理论上不应该进入这条分支),返回null
  257 + showToast('下个课程还没解锁哦');
  258 + return null;
  259 + }
  260 + });
  261 + return result;
  262 + }
192 263 } else {
193   - ///section为空说明当前unit学完了,找下一个unit。(跨unit选lesson)
  264 + ///section为空说明当前unit学完了,找下一个unit。(跨unit选section)
194 265 ///先根据courseLessonId找出当前的unit
195 266 final curCourseUnitDetail = findCourseUnitDetailById(courseLessonId);
196 267 if (curCourseUnitDetail != null) {
197   - ///再根据当前unit找出下一个unit
198   - final nextCourseUnitDetail = _courseUnitEntity.courseUnitVOList
199   - ?.firstWhere((element) =>
200   - element.sortOrder == (curCourseUnitDetail.sortOrder! + 1));
  268 + ///再根据当前unit的sortOrder找出下一个unit
  269 + CourseUnitDetail? nextCourseUnitDetail =
  270 + _courseUnitEntity.courseUnitVOList?.firstWhereOrNull((element) =>
  271 + element.sortOrder == (curCourseUnitDetail.sortOrder! + 1));
  272 +
201 273 if (nextCourseUnitDetail != null) {
202   - final courseUnitId = nextCourseUnitDetail.id!;
203   - try {
204   - await loading(() async {
205   - _courseSectionDatasMap[courseUnitId] =
206   - await LessonDao.courseSection(courseUnitId: courseUnitId);
207   - emitter(LessonDataLoadState());
  274 + if (nextCourseUnitDetail.lock == true) {
  275 + ///如果下一个unit是锁定状态,请求数据刷新查询解锁状态
  276 + CourseSectionEntity? result = await loading(() async {
  277 + CourseUnitEntity? newCourseUnitEntity = await LessonDao.courseUnit(
  278 + _courseUnitEntity.nowCourseModuleId);
  279 +
  280 + ///拿到重新获取到的unit后,再次判断是否解锁
  281 + nextCourseUnitDetail = newCourseUnitEntity?.courseUnitVOList?.firstWhereOrNull(
  282 + (element) => element.id == nextCourseUnitDetail?.id);
  283 + if (nextCourseUnitDetail?.lock == false) {
  284 + ///解锁状态从锁定到解锁,覆盖原unit数据并刷新ui
  285 + _courseUnitEntity = newCourseUnitEntity!;
  286 + courseUnitEntityChanged = true;
  287 + emitter(LessonDataLoadState());
  288 +
  289 + return checkCourseSectionLockedOfNextUnit(courseLessonId, nextCourseUnitDetail!.id!, emitter);
  290 + } else {
  291 + showToast('下个单元课程还没解锁哦');
  292 +
  293 + ///如果还是锁定状态,返回null
  294 + return null;
  295 + }
208 296 });
209   - _pageController.nextPage(
210   - duration: const Duration(milliseconds: 500),
211   - curve: Curves.ease,
212   - );
213   - return _courseSectionDatasMap[courseUnitId]!.first;
214   - } catch (e) {
215   - if (e is ApiException) {
216   - showToast(e.message.toString());
217   - }
218   - return null;
  297 + return result;
  298 + } else {
  299 + return checkCourseSectionLockedOfNextUnit(courseLessonId, nextCourseUnitDetail.id!, emitter);
219 300 }
220 301 } else {
  302 + showToast("恭喜你,本阶段学到顶啦");
221 303 ///最后一个unit了
222 304 return null;
223 305 }
... ... @@ -227,4 +309,43 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
227 309 }
228 310 }
229 311 }
  312 +
  313 + ///检查下一个unit的(第一个)section
  314 + Future<CourseSectionEntity?> checkCourseSectionLockedOfNextUnit(int courseLessonId, int nextCourseUnitDetailId,
  315 + Emitter<SectionState> emitter) async {
  316 + CourseSectionEntity? firstSectionNextUnit = await getFirstSectionByUnitId(
  317 + nextCourseUnitDetailId, emitter);
  318 + if (firstSectionNextUnit != null) {
  319 + ///下个unit的第一个section如果不为空,再次检查是否锁定
  320 + CourseSectionEntity? courseSectionEntity = await checkCourseSectionLocked(courseLessonId, firstSectionNextUnit, emitter);
  321 + if (courseSectionEntity != null) {
  322 + ///只有是下一unit的第一个section并且解锁了,才跳转
  323 + _pageController.nextPage(
  324 + duration: const Duration(milliseconds: 250),
  325 + curve: Curves.ease,
  326 + );
  327 + }
  328 + return courseSectionEntity;
  329 + } else {
  330 + ///下个unit的第一个section如果为空,返回null
  331 + showToast('下个课程暂未找到');
  332 + return null;
  333 + }
  334 + }
  335 +
  336 + ///根据unitId获取当前unit的第一个section
  337 + Future<CourseSectionEntity?> getFirstSectionByUnitId(
  338 + int courseUnitId, Emitter<SectionState> emitter) async {
  339 + List<CourseSectionEntity>? courseSectionEntity =
  340 + _courseSectionDatasMap[courseUnitId];
  341 + if (courseSectionEntity == null) {
  342 + ///如果没下载过,请求数据
  343 + await loading(() async {
  344 + _courseSectionDatasMap[courseUnitId] =
  345 + await LessonDao.courseSection(courseUnitId: courseUnitId);
  346 + emitter(LessonDataLoadState());
  347 + });
  348 + }
  349 + return _courseSectionDatasMap[courseUnitId]?.first;
  350 + }
230 351 }
... ...
lib/pages/section/section_page.dart
... ... @@ -33,8 +33,14 @@ class SectionPage extends StatelessWidget {
33 33 ?.indexWhere((element) => element.id == courseUnitId) ??
34 34 0;
35 35 return BlocProvider(
36   - create: (context) => SectionBloc(courseUnitEntity, initialPage,
37   - PageController(initialPage: initialPage), ScrollController()),
  36 + create: (context) => SectionBloc(
  37 + courseUnitEntity,
  38 + initialPage,
  39 + PageController(initialPage: initialPage),
  40 + ScrollController(),
  41 + ScrollController()),
  42 + //为了触发指示器进入后计算位置
  43 + // ..add(CurrentUnitIndexChangeEvent(initialPage)),
38 44 child: _SectionPageView(context),
39 45 );
40 46 }
... ... @@ -146,78 +152,86 @@ class _SectionPageView extends StatelessWidget {
146 152 children: [
147 153 SectionHeaderWidget(
148 154 title: bloc.getCourseUnitDetail().name,
149   - courseModuleCode: bloc.courseUnitEntity.courseModuleCode),
  155 + courseModuleCode: bloc.courseUnitEntity.courseModuleCode,
  156 + onBack: () {
  157 + popPage(data: {
  158 + 'needRefresh': bloc.courseUnitEntityChanged,
  159 + });
  160 + }),
150 161 Expanded(
151   - child: Container(
152   - color: Colors.blue,
153   - child: Padding(
154   - padding: EdgeInsets.symmetric(horizontal: 10.w),
155   - // child: OverflowBox(
156   - child: NestedPageView.builder(
157   - itemCount: bloc.unlockPageCount(),
158   - controller: bloc.pageController,
159   - onPageChanged: (int index) {
160   - bloc.add(CurrentUnitIndexChangeEvent(index));
161   - },
162   - itemBuilder: (context, index) {
163   - // return ScrollConfiguration(
164   - // ///去掉 Android 上默认的边缘拖拽效果
165   - // behavior: ScrollConfiguration.of(context)
166   - // .copyWith(overscroll: false),
167   - // child: _itemTransCard(
168   - // bloc.getCourseUnitDetail(pageIndex: index),
169   - // index,
170   - // context),
171   - // );
172   - return _itemTransCard(
173   - bloc.getCourseUnitDetail(pageIndex: index),
174   - index,
175   - context);
176   - }),
177   - // ), // 设置外部padding,
178   - )
179   - )
180   - ),
  162 + child: Padding(
  163 + padding: EdgeInsets.symmetric(horizontal: 10.w),
  164 + child: NestedPageView.builder(
  165 + itemCount: bloc.unlockPageCount(),
  166 + controller: bloc.pageController,
  167 + onPageChanged: (int index) {
  168 + bloc.add(CurrentUnitIndexChangeEvent(index));
  169 + },
  170 + itemBuilder: (context, index) {
  171 + return ScrollConfiguration(
  172 + ///去掉 Android 上默认的边缘拖拽效果
  173 + behavior: ScrollConfiguration.of(context)
  174 + .copyWith(overscroll: false),
  175 + child: _itemTransCard(
  176 + bloc.getCourseUnitDetail(pageIndex: index),
  177 + index,
  178 + context),
  179 + );
  180 + }),
  181 + )),
181 182 SafeArea(
182   - child: Padding(
183   - padding: EdgeInsets.symmetric(horizontal: 13.w),
184   - child: Row(
185   - mainAxisAlignment: MainAxisAlignment.spaceBetween,
186   - children: [
187   - SizedBox(
188   - height: 47.h,
189   - width: 80.w,
190   - ),
191   - Container(
192   - decoration: BoxDecoration(
193   - color: CourseModuleModel(
194   - bloc.courseUnitEntity.courseModuleCode ??
195   - 'Phase-1')
196   - .color,
197   - borderRadius: BorderRadius.circular(14.5.r),
198   - ),
199   - padding: EdgeInsets.symmetric(
200   - vertical: 8.h, horizontal: 24.w),
201   - child: Text(
202   - '${bloc.currentPage + 1}/${bloc.unlockPageCount()}',
203   - style: TextStyle(
204   - color: Colors.white, fontSize: 12.sp),
205   - ),
206   - ),
207   - Image.asset(
208   - CourseModuleModel(
209   - bloc.courseUnitEntity.courseModuleCode ??
210   - 'Phase-1')
211   - .courseModuleLogo
212   - .assetPng,
213   - height: 47.h,
214   - width: 80.w,
215   - // color: Colors.red,
216   - ),
217   - ],
218   - ),
219   - ),
220   - )
  183 + child: SizedBox(
  184 + width: 210.w,
  185 + height: 40.h,
  186 + // child: OverflowBox(
  187 + // maxWidth: 300, // 允许内容超出容器宽度
  188 + child: ListView.builder(
  189 + controller: bloc.indicatorSrollController,
  190 + scrollDirection: Axis.horizontal,
  191 + itemCount: bloc.unlockPageCount(),
  192 + itemBuilder: (context, index) {
  193 + bool isSelected = index == bloc.currentPage;
  194 + // 计算透明度,使得当前页的指示器不透明,两侧逐渐透明
  195 + double opacity = 0.25 +
  196 + (1 - ((bloc.currentPage - index).abs() / 2)) *
  197 + 0.5;
  198 + // 限制透明度在 0.5 到 1.0 之间
  199 + opacity = opacity.clamp(0.25, 1.0);
  200 + return GestureDetector(
  201 + onTap: () => bloc.pageController.animateToPage(
  202 + index,
  203 + duration: const Duration(milliseconds: 60),
  204 + curve: Curves.ease),
  205 + child: SizedBox(
  206 + width: 30.0.w,
  207 + height: 30.0.h,
  208 + child: Padding(
  209 + padding:
  210 + EdgeInsets.all(isSelected ? 0.w : 5.w),
  211 + child: Opacity(
  212 + opacity: opacity,
  213 + child: Container(
  214 + alignment: Alignment.center,
  215 + decoration: BoxDecoration(
  216 + color: isSelected
  217 + ? Colors.blue
  218 + : Colors.grey,
  219 + shape: BoxShape.circle,
  220 + ),
  221 + child: Text("${index + 1}",
  222 + textAlign: TextAlign.center,
  223 + style: TextStyle(
  224 + color: Colors.white,
  225 + fontSize:
  226 + isSelected ? 12.sp : 8.sp)),
  227 + ),
  228 + ),
  229 + ),
  230 + ),
  231 + );
  232 + })),
  233 + ),
  234 + // )
221 235 ],
222 236 ),
223 237 ),
... ... @@ -252,62 +266,54 @@ Widget _itemTransCard(
252 266 ),
253 267 );
254 268 } else {
255   - return
256   - Padding(padding: EdgeInsets.symmetric(
257   - vertical: 28.h),
258   - child: Container(
259   - color: Colors.red,
260   - margin: EdgeInsets.symmetric(vertical: 10.h),
261   - child: NestedListView.builder(
262   - itemCount: bloc.courseSectionDatasMap[courseUnitDetail.id]?.length ?? 0,
263   - scrollDirection: Axis.horizontal,
264   - itemBuilder: (BuildContext context, int index) {
265   - CourseSectionEntity sectionData = courseSectionEntities[index];
266   - if (sectionData.courseType == SectionType.bouns.value) {
267   - //彩蛋
268   - return GestureDetector(
269   - onTap: () {
270   - if (!UserUtil.isLogined()) {
271   - pushNamed(AppRouteName.login);
272   - return;
273   - }
274   - if (sectionData.lock == true) {
275   - showToast('当前课程暂未解锁');
276   - return;
277   - }
  269 + return NestedListView.builder(
  270 + itemCount: bloc.courseSectionDatasMap[courseUnitDetail.id]?.length ?? 0,
  271 + scrollDirection: Axis.horizontal,
  272 + itemBuilder: (BuildContext context, int index) {
  273 + CourseSectionEntity sectionData = courseSectionEntities[index];
  274 + if (sectionData.courseType == SectionType.bouns.value) {
  275 + //彩蛋
  276 + return GestureDetector(
  277 + onTap: () {
  278 + if (!UserUtil.isLogined()) {
  279 + pushNamed(AppRouteName.login);
  280 + return;
  281 + }
  282 + if (sectionData.lock == true) {
  283 + showToast('当前课程暂未解锁');
  284 + return;
  285 + }
278 286  
279   - ///进入课堂
280   - bloc.add(RequestEnterClassEvent(
281   - sectionData.id.toString(), sectionData.courseType));
282   - },
283   - child: SectionBoundsItem(
284   - imageUrl: sectionData.coverUrl,
285   - ),
286   - );
287   - } else {
288   - return GestureDetector(
289   - onTap: () {
290   - if (!UserUtil.isLogined()) {
291   - pushNamed(AppRouteName.login);
292   - return;
293   - }
294   - if (sectionData.lock == true) {
295   - showToast('当前课程暂未解锁');
296   - return;
297   - }
  287 + ///进入课堂
  288 + bloc.add(RequestEnterClassEvent(
  289 + sectionData.id.toString(), sectionData.courseType));
  290 + },
  291 + child: SectionBoundsItem(
  292 + imageUrl: sectionData.coverUrl,
  293 + ),
  294 + );
  295 + } else {
  296 + return GestureDetector(
  297 + onTap: () {
  298 + if (!UserUtil.isLogined()) {
  299 + pushNamed(AppRouteName.login);
  300 + return;
  301 + }
  302 + if (sectionData.lock == true) {
  303 + showToast('当前课程暂未解锁');
  304 + return;
  305 + }
298 306  
299   - ///进入课堂
300   - bloc.add(RequestEnterClassEvent(
301   - sectionData.id.toString(), sectionData.courseType));
302   - },
303   - child: SectionItem(
304   - courseModuleId: bloc.courseUnitEntity.courseModuleCode,
305   - lessons: sectionData,
306   - ),
307   - );
308   - }
309   - }),
310   - ),
311   - );
  307 + ///进入课堂
  308 + bloc.add(RequestEnterClassEvent(
  309 + sectionData.id.toString(), sectionData.courseType));
  310 + },
  311 + child: SectionItem(
  312 + courseModuleId: bloc.courseUnitEntity.courseModuleCode,
  313 + lessons: sectionData,
  314 + ),
  315 + );
  316 + }
  317 + });
312 318 }
313 319 }
... ...
lib/pages/section/widgets/section_header_widget.dart
... ... @@ -7,12 +7,14 @@ import &#39;package:wow_english/pages/user/bloc/user_bloc.dart&#39;;
7 7 import '../courese_module_model.dart';
8 8  
9 9 class SectionHeaderWidget extends StatelessWidget {
10   - const SectionHeaderWidget({super.key, this.title, this.courseModuleCode});
  10 + const SectionHeaderWidget({super.key, this.title, this.courseModuleCode, this.onBack});
11 11  
12 12 final String? title;
13 13  
14 14 final String? courseModuleCode;
15 15  
  16 + final VoidCallback? onBack;
  17 +
16 18 @override
17 19 Widget build(BuildContext context) {
18 20 return BlocBuilder<UserBloc, UserState>(
... ... @@ -27,7 +29,11 @@ class SectionHeaderWidget extends StatelessWidget {
27 29 ScreenUtil().bottomBarHeight.horizontalSpace,
28 30 GestureDetector(
29 31 onTap: () {
30   - Navigator.pop(context);
  32 + if (onBack == null) {
  33 + Navigator.pop(context);
  34 + } else {
  35 + onBack!();
  36 + }
31 37 },
32 38 child: Container(
33 39 alignment: Alignment.center,
... ...
lib/pages/unit/bloc.dart
... ... @@ -23,10 +23,10 @@ class UnitBloc extends Bloc&lt;UnitEvent, UnitState&gt; {
23 23  
24 24  
25 25 UnitBloc(CourseModuleEntity? courseEntity) : super(UnitState().init()) {
26   - on<RequestUnitDataEvent>(_requestData);
  26 + on<RequestUnitDataEvent>(_requestUnitDatas);
27 27 }
28 28  
29   - void _requestData(RequestUnitDataEvent event, Emitter<UnitState> emitter) async {
  29 + void _requestUnitDatas(RequestUnitDataEvent event, Emitter<UnitState> emitter) async {
30 30 try {
31 31 await loading(() async {
32 32 _unitData = await LessonDao.courseUnit(event.moduleId);
... ...
lib/pages/unit/view.dart
... ... @@ -57,7 +57,7 @@ class UnitPage extends StatelessWidget {
57 57 return GestureDetector(
58 58 onTap: () {
59 59 if (data.lock == true) {
60   - showToast('当前课程暂未解锁');
  60 + showToast('当前单元课程暂未解锁');
61 61 return;
62 62 }
63 63  
... ... @@ -65,7 +65,15 @@ class UnitPage extends StatelessWidget {
65 65 arguments: {
66 66 'courseUnitEntity': bloc.unitData,
67 67 'courseUnitId': data.id
68   - });
  68 + }).then((value) {
  69 + if (value != null) {
  70 + Map<String, dynamic> dataMap = value as Map<String, dynamic>;
  71 + bool needRefresh = dataMap['needRefresh'];
  72 + if (needRefresh) {
  73 + bloc.add(RequestUnitDataEvent(courseModuleEntity?.id));
  74 + }
  75 + }
  76 + });
69 77 },
70 78 child: CourseUnitItem(
71 79 unitEntity: bloc.unitData!,
... ...
lib/utils/list_ext.dart
... ... @@ -8,4 +8,14 @@ extension ListExtension&lt;E&gt; on List&lt;E&gt; {
8 8 }
9 9 return null;
10 10 }
  11 +
  12 + /// 获取数组中第一个匹配元素,没有就返回null
  13 + E? firstWhereOrNull(bool Function(E element) test) {
  14 + for (E element in this) {
  15 + if (test(element)) {
  16 + return element;
  17 + }
  18 + }
  19 + return null;
  20 + }
11 21 }
... ...