Commit 46675a89f53e32a1b709d8512fbff8971f33ead9
1 parent
e3a0f013
feat:过渡页-视频环节
Showing
20 changed files
with
266 additions
and
42 deletions
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 | 89 | /// 进入课堂 |
90 | 90 | static const String enterClass = 'course/enter/class'; |
91 | 91 | |
92 | - /// 退出课堂 | |
92 | + /// 退出课堂(非完整、中断) | |
93 | 93 | static const String exitClass = 'course/exit/class'; |
94 | 94 | |
95 | + /// 结束课堂(完整) | |
96 | + static const String endClass = 'course/end/class'; | |
97 | + | |
95 | 98 | /// 商品列表 |
96 | 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 | 4 | import 'package:wow_english/models/listen_entity.dart'; |
5 | 5 | |
6 | 6 | import '../../../models/read_content_entity.dart'; |
7 | +import '../../../utils/date_util.dart'; | |
7 | 8 | |
8 | 9 | class ListenDao { |
9 | 10 | /// 磨耳朵 |
... | ... | @@ -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 | 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 | 49 | SectionBloc(this._courseUnitEntity, this._courseUnitDetail, this._pageController, this._listController) : super(LessonInitial()) { |
50 | 50 | on<RequestDataEvent>(_requestData); |
51 | 51 | on<RequestExitClassEvent>(_requestExitClass); |
52 | + on<RequestEndClassEvent>(_requestEndClass); | |
52 | 53 | on<RequestEnterClassEvent>(_requestEnterClass); |
53 | 54 | on<RequestVideoLessonEvent>(_requestVideoLesson); |
54 | 55 | on<CurrentUnitIndexChangeEvent>(_pageControllerChange); |
... | ... | @@ -95,7 +96,16 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { |
95 | 96 | } |
96 | 97 | |
97 | 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 | 111 | void _pageControllerChange(CurrentUnitIndexChangeEvent event, |
... | ... | @@ -108,4 +118,29 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { |
108 | 118 | int unlockPageCount() { |
109 | 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 | 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 | 41 | class CurrentUnitIndexChangeEvent extends SectionEvent { |
32 | 42 | final int unitIndex; | ... | ... |
lib/pages/section/section_page.dart
... | ... | @@ -6,6 +6,7 @@ import 'package:nested_scroll_views/material.dart'; |
6 | 6 | import 'package:wow_english/common/core/user_util.dart'; |
7 | 7 | import 'package:wow_english/common/extension/string_extension.dart'; |
8 | 8 | import 'package:wow_english/models/course_unit_entity.dart'; |
9 | +import 'package:wow_english/pages/section/section_type.dart'; | |
9 | 10 | import 'package:wow_english/pages/section/widgets/home_video_item.dart'; |
10 | 11 | import 'package:wow_english/pages/section/widgets/section_bouns_item.dart'; |
11 | 12 | import 'package:wow_english/pages/section/widgets/section_header_widget.dart'; |
... | ... | @@ -52,15 +53,15 @@ class _SectionPageView extends StatelessWidget { |
52 | 53 | if (state is RequestVideoLessonState) { |
53 | 54 | final videoUrl = bloc.processEntity?.videos?.videoUrl ?? ''; |
54 | 55 | var title = ''; |
55 | - if (state.type == 1) { | |
56 | + if (state.type == SectionType.song.value) { | |
56 | 57 | title = 'song'; |
57 | 58 | } |
58 | 59 | |
59 | - if (state.type == 2) { | |
60 | + if (state.type == SectionType.video.value) { | |
60 | 61 | title = 'video'; |
61 | 62 | } |
62 | 63 | |
63 | - if (state.type == 5) { | |
64 | + if (state.type == SectionType.bouns.value) { | |
64 | 65 | title = 'bonus'; |
65 | 66 | } |
66 | 67 | |
... | ... | @@ -70,22 +71,21 @@ class _SectionPageView extends StatelessWidget { |
70 | 71 | pushNamed(AppRouteName.lookVideo, arguments: { |
71 | 72 | 'videoUrl': videoUrl, |
72 | 73 | 'title': title, |
73 | - 'courseLessonId': state.courseLessonId | |
74 | + 'courseLessonId': state.courseLessonId, | |
75 | + 'isTopic': true | |
74 | 76 | }).then((value) { |
75 | 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 | 83 | return; |
85 | 84 | } |
86 | 85 | |
87 | 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 | 91 | bloc.add(RequestVideoLessonEvent( |
... | ... | @@ -93,7 +93,7 @@ class _SectionPageView extends StatelessWidget { |
93 | 93 | return; |
94 | 94 | } |
95 | 95 | |
96 | - if (state.courseType == 4) { | |
96 | + if (state.courseType == SectionType.pictureBook.value) { | |
97 | 97 | //绘本 |
98 | 98 | pushNamed(AppRouteName.reading, |
99 | 99 | arguments: {'courseLessonId': state.courseLessonId}) |
... | ... | @@ -107,7 +107,7 @@ class _SectionPageView extends StatelessWidget { |
107 | 107 | return; |
108 | 108 | } |
109 | 109 | |
110 | - if (state.courseType == 3) { | |
110 | + if (state.courseType == SectionType.practice.value) { | |
111 | 111 | //练习 |
112 | 112 | pushNamed(AppRouteName.topicPic, |
113 | 113 | arguments: {'courseLessonId': state.courseLessonId}) |
... | ... | @@ -214,7 +214,7 @@ Widget _itemTransCard(int index, BuildContext context) { |
214 | 214 | scrollDirection: Axis.horizontal, |
215 | 215 | itemBuilder: (BuildContext context, int index) { |
216 | 216 | CourseSectionEntity sectionData = bloc.courseSectionDatas![index]; |
217 | - if (sectionData.courseType == 5) { | |
217 | + if (sectionData.courseType == SectionType.bouns.value) { | |
218 | 218 | //彩蛋 |
219 | 219 | return GestureDetector( |
220 | 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
lib/pages/section/subsection/base_section/state.dart
0 → 100644
lib/pages/video/lookvideo/bloc/look_video_bloc.dart
1 | 1 | import 'package:flutter/cupertino.dart'; |
2 | 2 | import 'package:flutter_bloc/flutter_bloc.dart'; |
3 | 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 | 8 | part 'look_video_event.dart'; |
6 | 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 | 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 | 25 | on<LookVideoEvent>((event, emit) { |
14 | 26 | // TODO: implement event handler |
15 | 27 | }); | ... | ... |
lib/pages/video/lookvideo/bloc/look_video_event.dart
lib/pages/video/lookvideo/bloc/look_video_state.dart
lib/pages/video/lookvideo/look_video_page.dart
1 | 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 | 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 | 10 | final String? videoUrl; |
8 | 11 | final String? typeTitle; |
9 | 12 | final String? courseLessonId; |
13 | + final bool isTopic; | |
10 | 14 | |
11 | 15 | @override |
12 | - State<StatefulWidget> createState() { | |
13 | - return _LookVideoPageState(); | |
14 | - } | |
15 | -} | |
16 | - | |
17 | -class _LookVideoPageState extends State<LookVideoPage> { | |
18 | - @override | |
19 | 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 | 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 | 1 | import 'package:common_utils/common_utils.dart'; |
2 | 2 | import 'package:flutter/material.dart'; |
3 | +import 'package:flutter_bloc/flutter_bloc.dart'; | |
3 | 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; |
4 | 5 | import 'package:video_player/video_player.dart'; |
5 | 6 | import 'package:wow_english/common/extension/string_extension.dart'; |
7 | +import 'package:wow_english/pages/video/lookvideo/bloc/look_video_bloc.dart'; | |
6 | 8 | import 'package:wow_english/route/route.dart'; |
7 | 9 | |
8 | 10 | import 'video_opera_widget.dart'; |
9 | 11 | |
10 | 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 | 15 | final String videoUrl; |
14 | 16 | final String? typeTitle; |
15 | 17 | final String courseLessonId; |
18 | + final bool isTopic; | |
16 | 19 | |
17 | 20 | @override |
18 | 21 | State<StatefulWidget> createState() { |
... | ... | @@ -51,6 +54,12 @@ class _VideoWidgetState extends State<VideoWidget> { |
51 | 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<VideoWidget> { |
114 | 123 | setState(() { |
115 | 124 | _currentTime = formatDuration(_controller!.value.position); |
116 | 125 | _totalTime = formatDuration(_controller!.value.duration); |
117 | - _controller!.setLooping(true); | |
126 | + _controller!.setLooping(!widget.isTopic); | |
118 | 127 | _controller!.setVolume(100); |
119 | 128 | _controller!.play(); |
120 | 129 | }); | ... | ... |
lib/route/route.dart
... | ... | @@ -190,11 +190,14 @@ class AppRouter { |
190 | 190 | final title = (settings.arguments as Map)['title'] as String?; |
191 | 191 | final courseLessonId = |
192 | 192 | (settings.arguments as Map)['courseLessonId'] as String?; |
193 | + ///是否是课程内的视频环节,用于播放结束判断要不要再来一次以及下一环节用 | |
194 | + final isTopic = (settings.arguments as Map)['isTopic'] as bool? ?? false; | |
193 | 195 | return CupertinoPageRoute( |
194 | 196 | builder: (_) => LookVideoPage( |
195 | 197 | videoUrl: videoUrl, |
196 | 198 | typeTitle: title, |
197 | 199 | courseLessonId: courseLessonId, |
200 | + isTopic: isTopic, | |
198 | 201 | )); |
199 | 202 | /*case AppRouteName.setPwd: |
200 | 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 | 15 | \ No newline at end of file | ... | ... |
pubspec.yaml
... | ... | @@ -90,7 +90,7 @@ dependencies: |
90 | 90 | # 富文本插件 https://pub.dev/packages/extended_text |
91 | 91 | extended_text: ^11.0.1 |
92 | 92 | # 视频播放 https://pub.dev/packages/video_player |
93 | - video_player: ^2.7.0 | |
93 | + video_player: ^2.8.6 | |
94 | 94 | # UI适配 https://pub.dev/packages/responsive_framework |
95 | 95 | responsive_framework: ^1.0.0 |
96 | 96 | # 音频播放 https://pub.dev/packages/audioplayers | ... | ... |