Commit 46675a89f53e32a1b709d8512fbff8971f33ead9

Authored by 吴启风
1 parent e3a0f013

feat:过渡页-视频环节

assets/images/section_finish_again.png 0 → 100644

17.3 KB

assets/images/section_finish_next.png 0 → 100644

13.8 KB

assets/images/section_finish_steve.png 0 → 100644

34.7 KB

lib/common/request/apis.dart
@@ -89,9 +89,12 @@ class Apis { @@ -89,9 +89,12 @@ class Apis {
89 /// 进入课堂 89 /// 进入课堂
90 static const String enterClass = 'course/enter/class'; 90 static const String enterClass = 'course/enter/class';
91 91
92 - /// 退出课堂 92 + /// 退出课堂(非完整、中断)
93 static const String exitClass = 'course/exit/class'; 93 static const String exitClass = 'course/exit/class';
94 94
  95 + /// 结束课堂(完整)
  96 + static const String endClass = 'course/end/class';
  97 +
95 /// 商品列表 98 /// 商品列表
96 static const String productList = 'order/course/combo/list'; 99 static const String productList = 'order/course/combo/list';
97 100
lib/common/request/dao/listen_dao.dart
@@ -4,6 +4,7 @@ import 'package:wow_english/models/follow_read_entity.dart'; @@ -4,6 +4,7 @@ import 'package:wow_english/models/follow_read_entity.dart';
4 import 'package:wow_english/models/listen_entity.dart'; 4 import 'package:wow_english/models/listen_entity.dart';
5 5
6 import '../../../models/read_content_entity.dart'; 6 import '../../../models/read_content_entity.dart';
  7 +import '../../../utils/date_util.dart';
7 8
8 class ListenDao { 9 class ListenDao {
9 /// 磨耳朵 10 /// 磨耳朵
@@ -43,8 +44,14 @@ class ListenDao { @@ -43,8 +44,14 @@ class ListenDao {
43 } 44 }
44 45
45 ///退出课堂 46 ///退出课堂
46 - static Future exitClass(courseLessonId,currentStep,currentTime) async {  
47 - var data = await requestClient.post(Apis.exitClass,data: {'courseLessonId':courseLessonId,'currentStep':currentStep,'currentTime':currentTime}); 47 + static Future exitClass(courseLessonId,currentStep,{int? currentTime}) async {
  48 + var data = await requestClient.post(Apis.exitClass,data: {'courseLessonId':courseLessonId,'currentStep':currentStep,'currentTime':currentTime ?? getTimestampOfSecond()});
  49 + return data;
  50 + }
  51 +
  52 + ///完成课堂
  53 + static Future endClass(courseLessonId,currentStep,{int? currentTime}) async {
  54 + var data = await requestClient.post(Apis.endClass,data: {'courseLessonId':courseLessonId,'currentStep':currentStep,'currentTime':currentTime ?? getTimestampOfSecond()});
48 return data; 55 return data;
49 } 56 }
50 } 57 }
lib/pages/section/bloc/section_bloc.dart
@@ -49,6 +49,7 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { @@ -49,6 +49,7 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> {
49 SectionBloc(this._courseUnitEntity, this._courseUnitDetail, this._pageController, this._listController) : super(LessonInitial()) { 49 SectionBloc(this._courseUnitEntity, this._courseUnitDetail, this._pageController, this._listController) : super(LessonInitial()) {
50 on<RequestDataEvent>(_requestData); 50 on<RequestDataEvent>(_requestData);
51 on<RequestExitClassEvent>(_requestExitClass); 51 on<RequestExitClassEvent>(_requestExitClass);
  52 + on<RequestEndClassEvent>(_requestEndClass);
52 on<RequestEnterClassEvent>(_requestEnterClass); 53 on<RequestEnterClassEvent>(_requestEnterClass);
53 on<RequestVideoLessonEvent>(_requestVideoLesson); 54 on<RequestVideoLessonEvent>(_requestVideoLesson);
54 on<CurrentUnitIndexChangeEvent>(_pageControllerChange); 55 on<CurrentUnitIndexChangeEvent>(_pageControllerChange);
@@ -95,7 +96,16 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -95,7 +96,16 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
95 } 96 }
96 97
97 void _requestExitClass(RequestExitClassEvent event,Emitter<SectionState> emitter) async { 98 void _requestExitClass(RequestExitClassEvent event,Emitter<SectionState> emitter) async {
98 - await ListenDao.exitClass(event.courseLessonId,event.currentStep,event.currentTime); 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);
  104 + if (event.autoNextSection) {
  105 + final nextCourseSection = getNextCourseSectionBySort(int.parse(event.courseLessonId));
  106 + ///进入课堂
  107 + add(RequestEnterClassEvent(nextCourseSection!.id.toString() ?? '', nextCourseSection.courseType));
  108 + }
99 } 109 }
100 110
101 void _pageControllerChange(CurrentUnitIndexChangeEvent event, 111 void _pageControllerChange(CurrentUnitIndexChangeEvent event,
@@ -108,4 +118,29 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -108,4 +118,29 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
108 int unlockPageCount() { 118 int unlockPageCount() {
109 return _courseUnitEntity.courseUnitVOList?.indexWhereOrNull((element) => element.lock == true) ?? 1; 119 return _courseUnitEntity.courseUnitVOList?.indexWhereOrNull((element) => element.lock == true) ?? 1;
110 } 120 }
  121 +
  122 + CourseSectionEntity? getNextCourseSectionBySort(int courseLessonId) {
  123 + final curCourseSectionEntity = _courseSectionDatas?.firstWhere((element) => element.id == courseLessonId);
  124 + final curSort = curCourseSectionEntity?.sortOrder ?? 0;
  125 + CourseSectionEntity? nextCourseSectionEntity = _courseSectionDatas?.firstWhere((element) => element.sortOrder == curSort + 1);
  126 + if (nextCourseSectionEntity != null) {
  127 + return nextCourseSectionEntity;
  128 + } else {
  129 + ///跨unit选lesson
  130 + final curCourseUnitDetail = _courseUnitEntity.courseUnitVOList?.firstWhere((element) => element.id == curCourseSectionEntity?.courseUnitId);
  131 + if (curCourseUnitDetail != null) {
  132 + final nextCourseUnitDetail = _courseUnitEntity.courseUnitVOList?.firstWhere((element) => element.sortOrder == 0);
  133 + if (nextCourseUnitDetail != null) {
  134 + ///pageView翻页了,可能需要预加载 todo
  135 + return null;
  136 + } else {
  137 + ///最后一个unit了
  138 + return null;
  139 + }
  140 + } else {
  141 + ///找不到对应的unitDetail,理论上不可能
  142 + return null;
  143 + }
  144 + }
  145 + }
111 } 146 }
lib/pages/section/bloc/section_event.dart
@@ -27,6 +27,16 @@ class RequestExitClassEvent extends SectionEvent { @@ -27,6 +27,16 @@ class RequestExitClassEvent extends SectionEvent {
27 RequestExitClassEvent(this.courseLessonId,this.currentStep,this.currentTime); 27 RequestExitClassEvent(this.courseLessonId,this.currentStep,this.currentTime);
28 } 28 }
29 29
  30 +///结束课堂
  31 +class RequestEndClassEvent extends SectionEvent {
  32 + final String courseLessonId;
  33 + final String currentStep;
  34 + final String currentTime;
  35 + ///自动进入下一环节
  36 + final bool autoNextSection;
  37 + RequestEndClassEvent(this.courseLessonId,this.currentStep,this.currentTime,{this.autoNextSection = false});
  38 +}
  39 +
30 ///页面切换 40 ///页面切换
31 class CurrentUnitIndexChangeEvent extends SectionEvent { 41 class CurrentUnitIndexChangeEvent extends SectionEvent {
32 final int unitIndex; 42 final int unitIndex;
lib/pages/section/section_page.dart
@@ -6,6 +6,7 @@ import &#39;package:nested_scroll_views/material.dart&#39;; @@ -6,6 +6,7 @@ import &#39;package:nested_scroll_views/material.dart&#39;;
6 import 'package:wow_english/common/core/user_util.dart'; 6 import 'package:wow_english/common/core/user_util.dart';
7 import 'package:wow_english/common/extension/string_extension.dart'; 7 import 'package:wow_english/common/extension/string_extension.dart';
8 import 'package:wow_english/models/course_unit_entity.dart'; 8 import 'package:wow_english/models/course_unit_entity.dart';
  9 +import 'package:wow_english/pages/section/section_type.dart';
9 import 'package:wow_english/pages/section/widgets/home_video_item.dart'; 10 import 'package:wow_english/pages/section/widgets/home_video_item.dart';
10 import 'package:wow_english/pages/section/widgets/section_bouns_item.dart'; 11 import 'package:wow_english/pages/section/widgets/section_bouns_item.dart';
11 import 'package:wow_english/pages/section/widgets/section_header_widget.dart'; 12 import 'package:wow_english/pages/section/widgets/section_header_widget.dart';
@@ -52,15 +53,15 @@ class _SectionPageView extends StatelessWidget { @@ -52,15 +53,15 @@ class _SectionPageView extends StatelessWidget {
52 if (state is RequestVideoLessonState) { 53 if (state is RequestVideoLessonState) {
53 final videoUrl = bloc.processEntity?.videos?.videoUrl ?? ''; 54 final videoUrl = bloc.processEntity?.videos?.videoUrl ?? '';
54 var title = ''; 55 var title = '';
55 - if (state.type == 1) { 56 + if (state.type == SectionType.song.value) {
56 title = 'song'; 57 title = 'song';
57 } 58 }
58 59
59 - if (state.type == 2) { 60 + if (state.type == SectionType.video.value) {
60 title = 'video'; 61 title = 'video';
61 } 62 }
62 63
63 - if (state.type == 5) { 64 + if (state.type == SectionType.bouns.value) {
64 title = 'bonus'; 65 title = 'bonus';
65 } 66 }
66 67
@@ -70,22 +71,21 @@ class _SectionPageView extends StatelessWidget { @@ -70,22 +71,21 @@ class _SectionPageView extends StatelessWidget {
70 pushNamed(AppRouteName.lookVideo, arguments: { 71 pushNamed(AppRouteName.lookVideo, arguments: {
71 'videoUrl': videoUrl, 72 'videoUrl': videoUrl,
72 'title': title, 73 'title': title,
73 - 'courseLessonId': state.courseLessonId 74 + 'courseLessonId': state.courseLessonId,
  75 + 'isTopic': true
74 }).then((value) { 76 }).then((value) {
75 if (value != null) { 77 if (value != null) {
76 - Map<String, String> dataMap = value as Map<String, String>;  
77 - bloc.add(RequestExitClassEvent(  
78 - dataMap['courseLessonId']!,  
79 - '0',  
80 - dataMap['currentTime']!,  
81 - )); 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));
82 } 81 }
83 }); 82 });
84 return; 83 return;
85 } 84 }
86 85
87 if (state is RequestEnterClassState) { 86 if (state is RequestEnterClassState) {
88 - if (state.courseType != 3 && state.courseType != 4) { 87 + if (state.courseType != SectionType.practice.value
  88 + && state.courseType != SectionType.pictureBook.value) {
89 ///视频类型 89 ///视频类型
90 ///获取视频课程内容 90 ///获取视频课程内容
91 bloc.add(RequestVideoLessonEvent( 91 bloc.add(RequestVideoLessonEvent(
@@ -93,7 +93,7 @@ class _SectionPageView extends StatelessWidget { @@ -93,7 +93,7 @@ class _SectionPageView extends StatelessWidget {
93 return; 93 return;
94 } 94 }
95 95
96 - if (state.courseType == 4) { 96 + if (state.courseType == SectionType.pictureBook.value) {
97 //绘本 97 //绘本
98 pushNamed(AppRouteName.reading, 98 pushNamed(AppRouteName.reading,
99 arguments: {'courseLessonId': state.courseLessonId}) 99 arguments: {'courseLessonId': state.courseLessonId})
@@ -107,7 +107,7 @@ class _SectionPageView extends StatelessWidget { @@ -107,7 +107,7 @@ class _SectionPageView extends StatelessWidget {
107 return; 107 return;
108 } 108 }
109 109
110 - if (state.courseType == 3) { 110 + if (state.courseType == SectionType.practice.value) {
111 //练习 111 //练习
112 pushNamed(AppRouteName.topicPic, 112 pushNamed(AppRouteName.topicPic,
113 arguments: {'courseLessonId': state.courseLessonId}) 113 arguments: {'courseLessonId': state.courseLessonId})
@@ -214,7 +214,7 @@ Widget _itemTransCard(int index, BuildContext context) { @@ -214,7 +214,7 @@ Widget _itemTransCard(int index, BuildContext context) {
214 scrollDirection: Axis.horizontal, 214 scrollDirection: Axis.horizontal,
215 itemBuilder: (BuildContext context, int index) { 215 itemBuilder: (BuildContext context, int index) {
216 CourseSectionEntity sectionData = bloc.courseSectionDatas![index]; 216 CourseSectionEntity sectionData = bloc.courseSectionDatas![index];
217 - if (sectionData.courseType == 5) { 217 + if (sectionData.courseType == SectionType.bouns.value) {
218 //彩蛋 218 //彩蛋
219 return GestureDetector( 219 return GestureDetector(
220 onTap: () { 220 onTap: () {
lib/pages/section/section_type.dart 0 → 100644
  1 +///环节类型
  2 +enum SectionType {
  3 + ///儿歌
  4 + song,
  5 +
  6 + ///视频
  7 + video,
  8 +
  9 + ///练习
  10 + practice,
  11 +
  12 + ///绘本
  13 + pictureBook,
  14 +
  15 + ///彩蛋
  16 + bouns
  17 +}
  18 +
  19 +extension SectionTypeExtension on SectionType {
  20 + int get value {
  21 + switch (this) {
  22 + case SectionType.song:
  23 + return 1;
  24 + case SectionType.video:
  25 + return 2;
  26 + case SectionType.practice:
  27 + return 3;
  28 + case SectionType.pictureBook:
  29 + return 4;
  30 + case SectionType.bouns:
  31 + return 5;
  32 + default:
  33 + throw ArgumentError('Unknown section type');
  34 + }
  35 + }
  36 +}
lib/pages/section/subsection/base_section/bloc.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:wow_english/common/extension/string_extension.dart';
  4 +
  5 +import '../../../../route/route.dart';
  6 +import 'event.dart';
  7 +import 'state.dart';
  8 +
  9 +abstract class BaseSectionBloc<E extends BaseSectionEvent,
  10 + S extends BaseSectionState> extends Bloc<E, S> {
  11 + BaseSectionBloc(super.initialState);
  12 +
  13 + bool isCompleteDialogShow = false;
  14 +
  15 + // 这里可以定义一些通用的逻辑
  16 + void completeSection(final VoidCallback? nextSectionTap) {
  17 + // 逻辑来标记步骤为已完成
  18 + // 比如更新状态
  19 + if (isCompleteDialogShow) {
  20 + return;
  21 + }
  22 + isCompleteDialogShow = true;
  23 + showDialog(
  24 + context: AppRouter.context,
  25 + barrierDismissible: false,
  26 + barrierColor: Colors.black54,
  27 + builder: (BuildContext context) {
  28 + return AlertDialog(
  29 + backgroundColor: Colors.transparent,
  30 + content: SizedBox(
  31 + width: double.infinity, // 宽度设置为无限,使其尽可能铺满屏幕
  32 + height: MediaQuery.of(context).size.height * 0.6, // 高度设置为屏幕高度的60%
  33 + child: Row(
  34 + mainAxisAlignment: MainAxisAlignment.spaceBetween, // 图片之间分配空间
  35 + children: <Widget>[
  36 + // 左侧可点击的图片
  37 + Expanded(
  38 + flex: 1,
  39 + child: GestureDetector(
  40 + onTap: () {
  41 + isCompleteDialogShow = false;
  42 + popPage();
  43 + add(SectionAgainEvent() as E);
  44 + },
  45 + child: Image.asset('section_finish_again'.assetPng),
  46 + ),
  47 + ),
  48 + // 中间的图片
  49 + Expanded(
  50 + flex: 2,
  51 + child: Image.asset('section_finish_steve'.assetPng),
  52 + ),
  53 + // 右侧可点击的图片
  54 + Expanded(
  55 + flex: 1,
  56 + child: GestureDetector(
  57 + onTap: () {
  58 + // 处理右侧图片的点击事件
  59 + isCompleteDialogShow = false;
  60 + popPage();
  61 + nextSectionTap!();
  62 + },
  63 + child: Image.asset('section_finish_next'.assetPng),
  64 + ),
  65 + ),
  66 + ],
  67 + ),
  68 + ),
  69 + );
  70 + },
  71 + );
  72 + }
  73 +}
lib/pages/section/subsection/base_section/event.dart 0 → 100644
  1 +abstract class BaseSectionEvent {}
  2 +
  3 +///环节完成(结束)
  4 +class SectionCompleted extends BaseSectionEvent {}
  5 +
  6 +///环节再来一次
  7 +class SectionAgainEvent extends BaseSectionEvent {}
  8 +
  9 +///下一个环节
  10 +class SectionNextEvent extends BaseSectionEvent {}
0 \ No newline at end of file 11 \ No newline at end of file
lib/pages/section/subsection/base_section/state.dart 0 → 100644
  1 +abstract class BaseSectionState {}
  2 +
  3 +class SectionCompleted extends BaseSectionState {}
lib/pages/video/lookvideo/bloc/look_video_bloc.dart
1 import 'package:flutter/cupertino.dart'; 1 import 'package:flutter/cupertino.dart';
2 import 'package:flutter_bloc/flutter_bloc.dart'; 2 import 'package:flutter_bloc/flutter_bloc.dart';
3 import 'package:video_player/video_player.dart'; 3 import 'package:video_player/video_player.dart';
  4 +import 'package:wow_english/pages/section/subsection/base_section/bloc.dart';
  5 +import 'package:wow_english/pages/section/subsection/base_section/event.dart';
  6 +import 'package:wow_english/pages/section/subsection/base_section/state.dart';
4 7
5 part 'look_video_event.dart'; 8 part 'look_video_event.dart';
6 part 'look_video_state.dart'; 9 part 'look_video_state.dart';
7 10
8 -class LookVideoBloc extends Bloc<LookVideoEvent, LookVideoState> { 11 +class LookVideoBloc extends BaseSectionBloc<LookVideoEvent, LookVideoState> {
9 12
10 VideoPlayerController? _controller; 13 VideoPlayerController? _controller;
11 14
12 - LookVideoBloc() : super(LookVideoInitial()) { 15 + final String? _videoUrl;
  16 + String? get videoUrl => _videoUrl;
  17 + final String? _typeTitle;
  18 + String? get typeTitle => _typeTitle;
  19 + final String? _courseLessonId;
  20 + String? get courseLessonId => _courseLessonId;
  21 + final bool _isTopic;
  22 + bool get isTopic => _isTopic;
  23 +
  24 + LookVideoBloc(this._videoUrl, this._typeTitle, this._courseLessonId, this._isTopic) : super(LookVideoInitial()) {
13 on<LookVideoEvent>((event, emit) { 25 on<LookVideoEvent>((event, emit) {
14 // TODO: implement event handler 26 // TODO: implement event handler
15 }); 27 });
lib/pages/video/lookvideo/bloc/look_video_event.dart
1 part of 'look_video_bloc.dart'; 1 part of 'look_video_bloc.dart';
2 2
3 @immutable 3 @immutable
4 -abstract class LookVideoEvent {} 4 +abstract class LookVideoEvent extends BaseSectionEvent {}
lib/pages/video/lookvideo/bloc/look_video_state.dart
1 part of 'look_video_bloc.dart'; 1 part of 'look_video_bloc.dart';
2 2
3 @immutable 3 @immutable
4 -abstract class LookVideoState {} 4 +abstract class LookVideoState extends BaseSectionState {}
5 5
6 class LookVideoInitial extends LookVideoState {} 6 class LookVideoInitial extends LookVideoState {}
7 7
lib/pages/video/lookvideo/look_video_page.dart
1 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:wow_english/pages/video/lookvideo/bloc/look_video_bloc.dart';
2 import 'package:wow_english/pages/video/lookvideo/widgets/video_widget.dart'; 4 import 'package:wow_english/pages/video/lookvideo/widgets/video_widget.dart';
3 5
4 -class LookVideoPage extends StatefulWidget {  
5 - const LookVideoPage({super.key, this.videoUrl, this.typeTitle, this.courseLessonId}); 6 +class LookVideoPage extends StatelessWidget {
  7 + const LookVideoPage(
  8 + {super.key, this.videoUrl, this.typeTitle, this.courseLessonId, this.isTopic = false});
6 9
7 final String? videoUrl; 10 final String? videoUrl;
8 final String? typeTitle; 11 final String? typeTitle;
9 final String? courseLessonId; 12 final String? courseLessonId;
  13 + final bool isTopic;
10 14
11 @override 15 @override
12 - State<StatefulWidget> createState() {  
13 - return _LookVideoPageState();  
14 - }  
15 -}  
16 -  
17 -class _LookVideoPageState extends State<LookVideoPage> {  
18 - @override  
19 Widget build(BuildContext context) { 16 Widget build(BuildContext context) {
20 - return Container(  
21 - color: Colors.white,  
22 - child: VideoWidget(  
23 - videoUrl: widget.videoUrl??'',  
24 - typeTitle: widget.typeTitle,  
25 - courseLessonId: widget.courseLessonId??'',  
26 - ), 17 + return BlocProvider(
  18 + create: (BuildContext context) => LookVideoBloc(videoUrl, typeTitle, courseLessonId, isTopic),
  19 + child: Builder(builder: (context) => _buildPage(context)),
27 ); 20 );
28 } 21 }
29 -}  
30 \ No newline at end of file 22 \ No newline at end of file
  23 +}
  24 +
  25 +Widget _buildPage(BuildContext context) {
  26 + return BlocBuilder<LookVideoBloc, LookVideoState>(builder: (context, state) {
  27 + final bloc = BlocProvider.of<LookVideoBloc>(context);
  28 + return Container(
  29 + color: Colors.white,
  30 + child: VideoWidget(
  31 + videoUrl: bloc.videoUrl ?? '',
  32 + typeTitle: bloc.typeTitle ?? '',
  33 + courseLessonId: bloc.courseLessonId ?? '',
  34 + isTopic: bloc.isTopic,
  35 + )
  36 + );
  37 + }
  38 + );
  39 +}
lib/pages/video/lookvideo/widgets/video_widget.dart
1 import 'package:common_utils/common_utils.dart'; 1 import 'package:common_utils/common_utils.dart';
2 import 'package:flutter/material.dart'; 2 import 'package:flutter/material.dart';
  3 +import 'package:flutter_bloc/flutter_bloc.dart';
3 import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 import 'package:flutter_screenutil/flutter_screenutil.dart';
4 import 'package:video_player/video_player.dart'; 5 import 'package:video_player/video_player.dart';
5 import 'package:wow_english/common/extension/string_extension.dart'; 6 import 'package:wow_english/common/extension/string_extension.dart';
  7 +import 'package:wow_english/pages/video/lookvideo/bloc/look_video_bloc.dart';
6 import 'package:wow_english/route/route.dart'; 8 import 'package:wow_english/route/route.dart';
7 9
8 import 'video_opera_widget.dart'; 10 import 'video_opera_widget.dart';
9 11
10 class VideoWidget extends StatefulWidget { 12 class VideoWidget extends StatefulWidget {
11 - const VideoWidget({super.key, this.videoUrl = '',this.typeTitle, this.courseLessonId = ''}); 13 + const VideoWidget({super.key, this.videoUrl = '',this.typeTitle, this.courseLessonId = '', this.isTopic = false});
12 14
13 final String videoUrl; 15 final String videoUrl;
14 final String? typeTitle; 16 final String? typeTitle;
15 final String courseLessonId; 17 final String courseLessonId;
  18 + final bool isTopic;
16 19
17 @override 20 @override
18 State<StatefulWidget> createState() { 21 State<StatefulWidget> createState() {
@@ -51,6 +54,12 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; { @@ -51,6 +54,12 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; {
51 _playDegree = 0.0; 54 _playDegree = 0.0;
52 } 55 }
53 }); 56 });
  57 + } else if (_controller!.value.isCompleted) {
  58 + context.read<LookVideoBloc>().completeSection((){
  59 + String currentTime = (_controller!.value.position.inMinutes.remainder(60)*60+_controller!.value.position.inSeconds.remainder(60)).toString();
  60 + popPage(data:{'courseLessonId':widget.courseLessonId,'currentTime':currentTime,
  61 + 'nextSection':widget.isTopic});
  62 + } as VoidCallback);
54 } 63 }
55 } 64 }
56 }); 65 });
@@ -114,7 +123,7 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; { @@ -114,7 +123,7 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; {
114 setState(() { 123 setState(() {
115 _currentTime = formatDuration(_controller!.value.position); 124 _currentTime = formatDuration(_controller!.value.position);
116 _totalTime = formatDuration(_controller!.value.duration); 125 _totalTime = formatDuration(_controller!.value.duration);
117 - _controller!.setLooping(true); 126 + _controller!.setLooping(!widget.isTopic);
118 _controller!.setVolume(100); 127 _controller!.setVolume(100);
119 _controller!.play(); 128 _controller!.play();
120 }); 129 });
lib/route/route.dart
@@ -190,11 +190,14 @@ class AppRouter { @@ -190,11 +190,14 @@ class AppRouter {
190 final title = (settings.arguments as Map)['title'] as String?; 190 final title = (settings.arguments as Map)['title'] as String?;
191 final courseLessonId = 191 final courseLessonId =
192 (settings.arguments as Map)['courseLessonId'] as String?; 192 (settings.arguments as Map)['courseLessonId'] as String?;
  193 + ///是否是课程内的视频环节,用于播放结束判断要不要再来一次以及下一环节用
  194 + final isTopic = (settings.arguments as Map)['isTopic'] as bool? ?? false;
193 return CupertinoPageRoute( 195 return CupertinoPageRoute(
194 builder: (_) => LookVideoPage( 196 builder: (_) => LookVideoPage(
195 videoUrl: videoUrl, 197 videoUrl: videoUrl,
196 typeTitle: title, 198 typeTitle: title,
197 courseLessonId: courseLessonId, 199 courseLessonId: courseLessonId,
  200 + isTopic: isTopic,
198 )); 201 ));
199 /*case AppRouteName.setPwd: 202 /*case AppRouteName.setPwd:
200 case AppRouteName.setPwd: 203 case AppRouteName.setPwd:
lib/utils/date_util.dart 0 → 100644
  1 +
  2 +///获取当前时间(单位:秒)
  3 +int getTimestampOfSecond() {
  4 + // 获取当前时间
  5 + DateTime now = DateTime.now();
  6 +
  7 + // 获取自Unix纪元以来的毫秒数
  8 + int milliseconds = now.millisecondsSinceEpoch;
  9 +
  10 + // 将毫秒数转换为秒
  11 + int seconds = milliseconds ~/ 1000;
  12 +
  13 + return seconds;
  14 +}
0 \ No newline at end of file 15 \ No newline at end of file
pubspec.yaml
@@ -90,7 +90,7 @@ dependencies: @@ -90,7 +90,7 @@ dependencies:
90 # 富文本插件 https://pub.dev/packages/extended_text 90 # 富文本插件 https://pub.dev/packages/extended_text
91 extended_text: ^11.0.1 91 extended_text: ^11.0.1
92 # 视频播放 https://pub.dev/packages/video_player 92 # 视频播放 https://pub.dev/packages/video_player
93 - video_player: ^2.7.0 93 + video_player: ^2.8.6
94 # UI适配 https://pub.dev/packages/responsive_framework 94 # UI适配 https://pub.dev/packages/responsive_framework
95 responsive_framework: ^1.0.0 95 responsive_framework: ^1.0.0
96 # 音频播放 https://pub.dev/packages/audioplayers 96 # 音频播放 https://pub.dev/packages/audioplayers