Commit 1e22b7d1bfc9bfeef388fe4b909281f759dd02d6

Authored by 吴启风
1 parent 26982eea

feat:儿歌/视频环节接口请求时机优化

lib/common/request/dao/lesson_dao.dart
@@ -13,6 +13,7 @@ class LessonDao { @@ -13,6 +13,7 @@ class LessonDao {
13 } 13 }
14 14
15 ///课程单元列表 15 ///课程单元列表
  16 + ///@param moduleId 模块id
16 static Future<CourseUnitEntity?> courseUnit(int? moduleId) async { 17 static Future<CourseUnitEntity?> courseUnit(int? moduleId) async {
17 Map<String, dynamic> mapData = {}; 18 Map<String, dynamic> mapData = {};
18 if (moduleId != null) { 19 if (moduleId != null) {
@@ -32,7 +33,7 @@ class LessonDao { @@ -32,7 +33,7 @@ class LessonDao {
32 return data; 33 return data;
33 } 34 }
34 35
35 - ///课程(单元)列表 36 + ///课程(环节)列表
36 static Future<List<CourseSectionEntity>?> courseSection({required int courseUnitId}) async { 37 static Future<List<CourseSectionEntity>?> courseSection({required int courseUnitId}) async {
37 Map<String, dynamic> mapData = {}; 38 Map<String, dynamic> mapData = {};
38 mapData['courseUnitId'] = courseUnitId; 39 mapData['courseUnitId'] = courseUnitId;
lib/pages/practice/bloc/topic_picture_bloc.dart
@@ -19,6 +19,7 @@ import &#39;../../../common/permission/permissionRequester.dart&#39;; @@ -19,6 +19,7 @@ import &#39;../../../common/permission/permissionRequester.dart&#39;;
19 import '../../../route/route.dart'; 19 import '../../../route/route.dart';
20 20
21 part 'topic_picture_event.dart'; 21 part 'topic_picture_event.dart';
  22 +
22 part 'topic_picture_state.dart'; 23 part 'topic_picture_state.dart';
23 24
24 enum VoicePlayState { 25 enum VoicePlayState {
@@ -47,6 +48,8 @@ class TopicPictureBloc @@ -47,6 +48,8 @@ class TopicPictureBloc
47 48
48 CourseProcessEntity? _entity; 49 CourseProcessEntity? _entity;
49 50
  51 + CourseProcessEntity? get entity => _entity;
  52 +
50 ///正在评测 53 ///正在评测
51 bool _isVoicing = false; 54 bool _isVoicing = false;
52 55
@@ -63,8 +66,6 @@ class TopicPictureBloc @@ -63,8 +66,6 @@ class TopicPictureBloc
63 66
64 bool get forbiddenWhenCorrect => _forbiddenWhenCorrect; 67 bool get forbiddenWhenCorrect => _forbiddenWhenCorrect;
65 68
66 - CourseProcessEntity? get entity => _entity;  
67 -  
68 int get currentPage => _currentPage + 1; 69 int get currentPage => _currentPage + 1;
69 70
70 int get selectItem => _selectItem; 71 int get selectItem => _selectItem;
@@ -257,8 +258,8 @@ class TopicPictureBloc @@ -257,8 +258,8 @@ class TopicPictureBloc
257 XSVoiceStartEvent event, Emitter<TopicPictureState> emitter) async { 258 XSVoiceStartEvent event, Emitter<TopicPictureState> emitter) async {
258 await audioPlayer.stop(); 259 await audioPlayer.stop();
259 // 调用封装好的权限检查和请求方法 260 // 调用封装好的权限检查和请求方法
260 - bool result =  
261 - await requestPermission(context, Permission.microphone, "录音", "用于开启录音,识别您的开口作答并给出反馈"); 261 + bool result = await requestPermission(
  262 + context, Permission.microphone, "录音", "用于开启录音,识别您的开口作答并给出反馈");
262 if (result) { 263 if (result) {
263 methodChannel.invokeMethod('startVoice', { 264 methodChannel.invokeMethod('startVoice', {
264 'word': event.testWord, 265 'word': event.testWord,
lib/pages/section/bloc/section_bloc.dart
1 -import 'package:audioplayers/audioplayers.dart';  
2 import 'package:flutter/cupertino.dart'; 1 import 'package:flutter/cupertino.dart';
3 import 'package:flutter/foundation.dart'; 2 import 'package:flutter/foundation.dart';
4 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
5 import 'package:flutter_bloc/flutter_bloc.dart'; 4 import 'package:flutter_bloc/flutter_bloc.dart';
6 import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 import 'package:flutter_screenutil/flutter_screenutil.dart';
7 -import 'package:wow_english/common/extension/string_extension.dart';  
8 import 'package:wow_english/common/request/dao/lesson_dao.dart'; 6 import 'package:wow_english/common/request/dao/lesson_dao.dart';
9 import 'package:wow_english/common/request/exception.dart'; 7 import 'package:wow_english/common/request/exception.dart';
10 import 'package:wow_english/common/request/dao/listen_dao.dart'; 8 import 'package:wow_english/common/request/dao/listen_dao.dart';
11 -import 'package:wow_english/models/course_process_entity.dart';  
12 import 'package:wow_english/utils/audio_player_util.dart'; 9 import 'package:wow_english/utils/audio_player_util.dart';
13 import 'package:wow_english/utils/loading.dart'; 10 import 'package:wow_english/utils/loading.dart';
14 import 'package:wow_english/utils/toast_util.dart'; 11 import 'package:wow_english/utils/toast_util.dart';
@@ -53,10 +50,6 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -53,10 +50,6 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
53 Map<int, List<CourseSectionEntity>?> get courseSectionDatasMap => 50 Map<int, List<CourseSectionEntity>?> get courseSectionDatasMap =>
54 _courseSectionDatasMap; 51 _courseSectionDatasMap;
55 52
56 - CourseProcessEntity? _processEntity;  
57 -  
58 - CourseProcessEntity? get processEntity => _processEntity;  
59 -  
60 ///点击环节后先请求数据再进入,标志位避免频繁点击多次请求(以及多次进入) 53 ///点击环节后先请求数据再进入,标志位避免频繁点击多次请求(以及多次进入)
61 bool _isRequesting = false; 54 bool _isRequesting = false;
62 55
@@ -66,7 +59,6 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -66,7 +59,6 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
66 on<RequestDataEvent>(_requestSectionsData); 59 on<RequestDataEvent>(_requestSectionsData);
67 on<RequestEndClassEvent>(_requestEndClass); 60 on<RequestEndClassEvent>(_requestEndClass);
68 on<RequestEnterClassEvent>(_requestEnterClass); 61 on<RequestEnterClassEvent>(_requestEnterClass);
69 - on<RequestVideoLessonEvent>(_requestVideoLesson);  
70 on<CurrentUnitIndexChangeEvent>(_pageControllerChange); 62 on<CurrentUnitIndexChangeEvent>(_pageControllerChange);
71 on<InitEvent>((event, emit) async { 63 on<InitEvent>((event, emit) async {
72 await AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.countWithMe); 64 await AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.countWithMe);
@@ -92,23 +84,6 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -92,23 +84,6 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
92 } 84 }
93 } 85 }
94 86
95 - void _requestVideoLesson(  
96 - RequestVideoLessonEvent event, Emitter<SectionState> emitter) async {  
97 - try {  
98 - await loading(() async {  
99 - _processEntity = await ListenDao.process(event.courseLessonId);  
100 - emitter(  
101 - RequestVideoLessonState(event.courseLessonId, event.courseType));  
102 - });  
103 - } catch (e) {  
104 - if (e is ApiException) {  
105 - showToast(e.message ?? '请求失败,请检查网络连接');  
106 - }  
107 - } finally {  
108 - _isRequesting = false;  
109 - }  
110 - }  
111 -  
112 void _requestEnterClass( 87 void _requestEnterClass(
113 RequestEnterClassEvent event, Emitter<SectionState> emitter) async { 88 RequestEnterClassEvent event, Emitter<SectionState> emitter) async {
114 try { 89 try {
@@ -124,8 +99,9 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -124,8 +99,9 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
124 } catch (e) { 99 } catch (e) {
125 if (e is ApiException) { 100 if (e is ApiException) {
126 showToast(e.message ?? '请求失败,请检查网络连接'); 101 showToast(e.message ?? '请求失败,请检查网络连接');
127 - _isRequesting = false;  
128 } 102 }
  103 + } finally {
  104 + _isRequesting = false;
129 } 105 }
130 } 106 }
131 107
lib/pages/section/bloc/section_event.dart
@@ -11,14 +11,6 @@ class RequestDataEvent extends SectionEvent { @@ -11,14 +11,6 @@ class RequestDataEvent extends SectionEvent {
11 11
12 class InitEvent extends SectionEvent {} 12 class InitEvent extends SectionEvent {}
13 13
14 -///获取视频课程内容  
15 -class RequestVideoLessonEvent extends SectionEvent {  
16 - final String courseLessonId;  
17 - final int courseType;  
18 -  
19 - RequestVideoLessonEvent(this.courseLessonId, this.courseType);  
20 -}  
21 -  
22 ///进入课堂 14 ///进入课堂
23 class RequestEnterClassEvent extends SectionEvent { 15 class RequestEnterClassEvent extends SectionEvent {
24 final String courseLessonId; 16 final String courseLessonId;
lib/pages/section/bloc/section_state.dart
@@ -7,13 +7,6 @@ class LessonInitial extends SectionState {} @@ -7,13 +7,6 @@ class LessonInitial extends SectionState {}
7 7
8 class LessonDataLoadState extends SectionState {} 8 class LessonDataLoadState extends SectionState {}
9 9
10 -class RequestVideoLessonState extends SectionState {  
11 - final String courseLessonId;  
12 - final int type;  
13 -  
14 - RequestVideoLessonState(this.courseLessonId, this.type);  
15 -}  
16 -  
17 class RequestEnterClassState extends SectionState { 10 class RequestEnterClassState extends SectionState {
18 final String courseLessonId; 11 final String courseLessonId;
19 final int courseType; 12 final int courseType;
lib/pages/section/section_page.dart
@@ -60,58 +60,36 @@ class _SectionPageView extends StatelessWidget { @@ -60,58 +60,36 @@ class _SectionPageView extends StatelessWidget {
60 final bloc = BlocProvider.of<SectionBloc>(context); 60 final bloc = BlocProvider.of<SectionBloc>(context);
61 return BlocListener<SectionBloc, SectionState>( 61 return BlocListener<SectionBloc, SectionState>(
62 listener: (context, state) async { 62 listener: (context, state) async {
63 - if (state is RequestVideoLessonState) {  
64 - final videoUrl = bloc.processEntity?.videos?.videoUrl ?? '';  
65 - var title = '';  
66 - if (state.type == SectionType.song.value) {  
67 - title = 'song';  
68 - }  
69 -  
70 - if (state.type == SectionType.video.value) {  
71 - title = 'video';  
72 - }  
73 -  
74 - if (state.type == SectionType.bouns.value) {  
75 - title = 'bonus';  
76 - }  
77 -  
78 - if (videoUrl.isEmpty || !videoUrl.contains('http')) {  
79 - return;  
80 - }  
81 - pushNamed(AppRouteName.lookVideo, arguments: {  
82 - 'videoUrl': videoUrl,  
83 - 'title': title,  
84 - 'courseLessonId': state.courseLessonId,  
85 - 'isTopic': true  
86 - }).then((value) {  
87 - if (value != null) {  
88 - Map<String, dynamic> dataMap = value as Map<String, dynamic>;  
89 - bloc.add(RequestEndClassEvent(  
90 - dataMap['courseLessonId']!, dataMap['isCompleted'],  
91 - currentTime: dataMap['currentTime'],  
92 - autoNextSection: dataMap['nextSection']));  
93 - }  
94 - AudioPlayerUtil.getInstance()  
95 - .playAudio(AudioPlayerUtilType.countWithMe);  
96 - });  
97 - return;  
98 - }  
99 -  
100 if (state is RequestEnterClassState) { 63 if (state is RequestEnterClassState) {
101 - if (state.courseType != SectionType.practice.value &&  
102 - state.courseType != SectionType.pictureBook.value) {  
103 - ///视频类型  
104 - ///获取视频课程内容  
105 - if (state.courseType == 1) {  
106 - AudioPlayerUtil.getInstance() 64 + if (state.courseType == SectionType.song.value &&
  65 + state.courseType == SectionType.video.value) {
  66 + var title =
  67 + state.courseType == SectionType.song.value ? 'song' : 'video';
  68 +
  69 + ///儿歌/视频类型
  70 + if (state.courseType == SectionType.song.value) {
  71 + await AudioPlayerUtil.getInstance()
107 .playAudio(AudioPlayerUtilType.musicTime); 72 .playAudio(AudioPlayerUtilType.musicTime);
108 } else { 73 } else {
109 - AudioPlayerUtil.getInstance() 74 + await AudioPlayerUtil.getInstance()
110 .playAudio(AudioPlayerUtilType.videoTime); 75 .playAudio(AudioPlayerUtilType.videoTime);
111 } 76 }
112 - bloc.add(RequestVideoLessonEvent(  
113 - state.courseLessonId, state.courseType));  
114 - 77 + pushNamed(AppRouteName.lookVideo, arguments: {
  78 + 'videoUrl': "",
  79 + 'title': title,
  80 + 'courseLessonId': state.courseLessonId,
  81 + 'isTopic': true
  82 + }).then((value) {
  83 + if (value != null) {
  84 + Map<String, dynamic> dataMap = value as Map<String, dynamic>;
  85 + bloc.add(RequestEndClassEvent(
  86 + dataMap['courseLessonId']!, dataMap['isCompleted'],
  87 + currentTime: dataMap['currentTime'],
  88 + autoNextSection: dataMap['nextSection']));
  89 + }
  90 + AudioPlayerUtil.getInstance()
  91 + .playAudio(AudioPlayerUtilType.countWithMe);
  92 + });
115 return; 93 return;
116 } 94 }
117 95
lib/pages/video/lookvideo/bloc/look_video_bloc.dart
@@ -5,15 +5,19 @@ import &#39;package:wow_english/pages/section/subsection/base_section/bloc.dart&#39;; @@ -5,15 +5,19 @@ import &#39;package:wow_english/pages/section/subsection/base_section/bloc.dart&#39;;
5 import 'package:wow_english/pages/section/subsection/base_section/event.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'; 6 import 'package:wow_english/pages/section/subsection/base_section/state.dart';
7 7
  8 +import '../../../../common/request/dao/listen_dao.dart';
  9 +import '../../../../common/request/exception.dart';
  10 +import '../../../../models/course_process_entity.dart';
  11 +import '../../../../utils/loading.dart';
  12 +import '../../../../utils/toast_util.dart';
  13 +
8 part 'look_video_event.dart'; 14 part 'look_video_event.dart';
9 part 'look_video_state.dart'; 15 part 'look_video_state.dart';
10 16
11 class LookVideoBloc extends BaseSectionBloc<BaseSectionEvent, BaseSectionState> { 17 class LookVideoBloc extends BaseSectionBloc<BaseSectionEvent, BaseSectionState> {
12 18
13 - VideoPlayerController? _controller;  
14 -  
15 final String? _videoUrl; 19 final String? _videoUrl;
16 - String? get videoUrl => _videoUrl; 20 + String? get videoUrl => _videoUrl ?? _entity?.videos?.videoUrl ?? '';
17 final String? _typeTitle; 21 final String? _typeTitle;
18 String? get typeTitle => _typeTitle; 22 String? get typeTitle => _typeTitle;
19 final String? _courseLessonId; 23 final String? _courseLessonId;
@@ -21,12 +25,32 @@ class LookVideoBloc extends BaseSectionBloc&lt;BaseSectionEvent, BaseSectionState&gt; @@ -21,12 +25,32 @@ class LookVideoBloc extends BaseSectionBloc&lt;BaseSectionEvent, BaseSectionState&gt;
21 final bool _isTopic; 25 final bool _isTopic;
22 bool get isTopic => _isTopic; 26 bool get isTopic => _isTopic;
23 27
  28 + CourseProcessEntity? _entity;
  29 +
  30 + CourseProcessEntity? get entity => _entity;
  31 +
24 LookVideoBloc(this._videoUrl, this._typeTitle, this._courseLessonId, this._isTopic) : super(LookVideoInitial()) { 32 LookVideoBloc(this._videoUrl, this._typeTitle, this._courseLessonId, this._isTopic) : super(LookVideoInitial()) {
25 on<LookVideoEvent>((event, emit) { 33 on<LookVideoEvent>((event, emit) {
26 // TODO: implement event handler 34 // TODO: implement event handler
27 }); 35 });
  36 + on<RequestDataEvent>(_requestData);
28 on<SectionAgainEvent>((event, emit) { 37 on<SectionAgainEvent>((event, emit) {
29 emit(SectionAgainState()); 38 emit(SectionAgainState());
30 }); 39 });
31 } 40 }
  41 +
  42 + ///请求数据
  43 + void _requestData(
  44 + RequestDataEvent event, Emitter<BaseSectionState> emitter) async {
  45 + try {
  46 + await loading(() async {
  47 + _entity = await ListenDao.process(courseLessonId);
  48 + emitter(RequestDataState());
  49 + });
  50 + } catch (e) {
  51 + if (e is ApiException) {
  52 + showToast(e.message ?? '请求失败,请检查网络连接');
  53 + }
  54 + }
  55 + }
32 } 56 }
lib/pages/video/lookvideo/bloc/look_video_event.dart
@@ -4,3 +4,5 @@ part of &#39;look_video_bloc.dart&#39;; @@ -4,3 +4,5 @@ part of &#39;look_video_bloc.dart&#39;;
4 abstract class LookVideoEvent extends BaseSectionEvent {} 4 abstract class LookVideoEvent extends BaseSectionEvent {}
5 5
6 class RestartVideoEvent extends LookVideoEvent {} 6 class RestartVideoEvent extends LookVideoEvent {}
  7 +
  8 +class RequestDataEvent extends LookVideoEvent {}
lib/pages/video/lookvideo/bloc/look_video_state.dart
@@ -6,3 +6,5 @@ abstract class LookVideoState extends BaseSectionState {} @@ -6,3 +6,5 @@ abstract class LookVideoState extends BaseSectionState {}
6 class LookVideoInitial extends LookVideoState {} 6 class LookVideoInitial extends LookVideoState {}
7 7
8 class VideoStarState extends LookVideoState {} 8 class VideoStarState extends LookVideoState {}
  9 +
  10 +class RequestDataState extends LookVideoState {}
lib/pages/video/lookvideo/look_video_page.dart
@@ -3,10 +3,15 @@ import &#39;package:flutter_bloc/flutter_bloc.dart&#39;; @@ -3,10 +3,15 @@ import &#39;package:flutter_bloc/flutter_bloc.dart&#39;;
3 import 'package:wow_english/pages/section/subsection/base_section/state.dart'; 3 import 'package:wow_english/pages/section/subsection/base_section/state.dart';
4 import 'package:wow_english/pages/video/lookvideo/bloc/look_video_bloc.dart'; 4 import 'package:wow_english/pages/video/lookvideo/bloc/look_video_bloc.dart';
5 import 'package:wow_english/pages/video/lookvideo/widgets/video_widget.dart'; 5 import 'package:wow_english/pages/video/lookvideo/widgets/video_widget.dart';
  6 +import 'package:wow_english/utils/log_util.dart';
6 7
7 class LookVideoPage extends StatelessWidget { 8 class LookVideoPage extends StatelessWidget {
8 const LookVideoPage( 9 const LookVideoPage(
9 - {super.key, this.videoUrl, this.typeTitle, this.courseLessonId, this.isTopic = false}); 10 + {super.key,
  11 + this.videoUrl,
  12 + this.typeTitle,
  13 + this.courseLessonId,
  14 + this.isTopic = false});
10 15
11 final String? videoUrl; 16 final String? videoUrl;
12 final String? typeTitle; 17 final String? typeTitle;
@@ -16,24 +21,34 @@ class LookVideoPage extends StatelessWidget { @@ -16,24 +21,34 @@ class LookVideoPage extends StatelessWidget {
16 @override 21 @override
17 Widget build(BuildContext context) { 22 Widget build(BuildContext context) {
18 return BlocProvider( 23 return BlocProvider(
19 - create: (BuildContext context) => LookVideoBloc(videoUrl, typeTitle, courseLessonId, isTopic), 24 + create: (BuildContext context) =>
  25 + LookVideoBloc(videoUrl, typeTitle, courseLessonId, isTopic)
  26 + ..add(RequestDataEvent()),
20 child: Builder(builder: (context) => _buildPage(context)), 27 child: Builder(builder: (context) => _buildPage(context)),
21 ); 28 );
22 } 29 }
23 } 30 }
24 31
25 Widget _buildPage(BuildContext context) { 32 Widget _buildPage(BuildContext context) {
26 - return BlocBuilder<LookVideoBloc, BaseSectionState>(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 - ); 33 + return BlocBuilder<LookVideoBloc, BaseSectionState>(
  34 + builder: (context, state) {
  35 + final bloc = BlocProvider.of<LookVideoBloc>(context);
  36 + Log.d("WQF lookvideo BlocBuilder bloc.videoUr=${bloc.videoUrl}");
  37 + return Center(
  38 + child: bloc.videoUrl?.isNotEmpty == true
  39 + ? Container(
  40 + color: Colors.white,
  41 + child: VideoWidget(
  42 + videoUrl: bloc.videoUrl ?? '',
  43 + typeTitle: bloc.typeTitle ?? '',
  44 + courseLessonId: bloc.courseLessonId ?? '',
  45 + isTopic: bloc.isTopic,
  46 + ))
  47 + //todo 空了需要抽一个通用的loading组件
  48 + : Container(
  49 + color: Colors.white,
  50 + child: const CircularProgressIndicator(),
  51 + ),
  52 + );
  53 + });
39 } 54 }
lib/pages/video/lookvideo/widgets/video_widget.dart
@@ -12,11 +12,12 @@ import &#39;../../../section/subsection/base_section/state.dart&#39;; @@ -12,11 +12,12 @@ import &#39;../../../section/subsection/base_section/state.dart&#39;;
12 import 'video_opera_widget.dart'; 12 import 'video_opera_widget.dart';
13 13
14 class VideoWidget extends StatefulWidget { 14 class VideoWidget extends StatefulWidget {
15 - const VideoWidget({super.key,  
16 - this.videoUrl = '',  
17 - this.typeTitle,  
18 - this.courseLessonId = '',  
19 - this.isTopic = false}); 15 + const VideoWidget(
  16 + {super.key,
  17 + this.videoUrl = '',
  18 + this.typeTitle,
  19 + this.courseLessonId = '',
  20 + this.isTopic = false});
20 21
21 final String videoUrl; 22 final String videoUrl;
22 final String? typeTitle; 23 final String? typeTitle;
@@ -39,10 +40,10 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; { @@ -39,10 +40,10 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; {
39 40
40 String formatDuration(Duration duration) { 41 String formatDuration(Duration duration) {
41 String hours = duration.inHours.toString().padLeft(2, '0'); 42 String hours = duration.inHours.toString().padLeft(2, '0');
42 - String minutes = duration.inMinutes.remainder(60).toString().padLeft(  
43 - 2, '0');  
44 - String seconds = duration.inSeconds.remainder(60).toString().padLeft(  
45 - 2, '0'); 43 + String minutes =
  44 + duration.inMinutes.remainder(60).toString().padLeft(2, '0');
  45 + String seconds =
  46 + duration.inSeconds.remainder(60).toString().padLeft(2, '0');
46 return "$hours:$minutes:$seconds"; 47 return "$hours:$minutes:$seconds";
47 } 48 }
48 49
@@ -71,17 +72,17 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; { @@ -71,17 +72,17 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; {
71 _controller!.value.position.inSeconds == 72 _controller!.value.position.inSeconds ==
72 _controller!.value.duration.inSeconds) { 73 _controller!.value.duration.inSeconds) {
73 final lookVideoBloc = context.read<LookVideoBloc>(); 74 final lookVideoBloc = context.read<LookVideoBloc>();
74 - lookVideoBloc.sectionComplete(() {  
75 - popPage(data: {  
76 - 'courseLessonId': widget.courseLessonId,  
77 - 'currentTime': getCurrentPositionSeconds(),  
78 - 'isCompleted': true,  
79 - 'nextSection': widget.isTopic  
80 - });  
81 - } as VoidCallback,  
82 - againSectionTap: (() {  
83 - lookVideoBloc.add(SectionAgainEvent());  
84 - }), context: context); 75 + lookVideoBloc.sectionComplete(
  76 + () {
  77 + popPage(data: {
  78 + 'courseLessonId': widget.courseLessonId,
  79 + 'currentTime': getCurrentPositionSeconds(),
  80 + 'isCompleted': true,
  81 + 'nextSection': widget.isTopic
  82 + });
  83 + } as VoidCallback, againSectionTap: (() {
  84 + lookVideoBloc.add(SectionAgainEvent());
  85 + }), context: context);
85 } 86 }
86 } 87 }
87 }); 88 });
@@ -189,63 +190,63 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; { @@ -189,63 +190,63 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; {
189 child: Center( 190 child: Center(
190 child: _controller!.value.isInitialized 191 child: _controller!.value.isInitialized
191 ? Stack( 192 ? Stack(
192 - alignment: Alignment.center,  
193 - children: [  
194 - SizedBox(  
195 - height: double.infinity,  
196 - width: double.infinity,  
197 - child: AspectRatio(  
198 - aspectRatio: _controller!.value.aspectRatio,  
199 - child: VideoPlayer(_controller!),  
200 - ),  
201 - ),  
202 - Offstage(  
203 - offstage: _hiddenTipView,  
204 - child: VideoOperaWidget(  
205 - title: widget.typeTitle ?? 'song',  
206 - degree: _playDegree,  
207 - totalTime: _totalTime,  
208 - currentTime: _currentTime,  
209 - isPlay: _controller!.value.isPlaying,  
210 - actionEvent: (OperationType type) {  
211 - actionType(type);  
212 - },  
213 - sliderChangeEvent: (double degree) {  
214 - int totalSecond = _controller!  
215 - .value.duration.inMinutes  
216 - .remainder(60) *  
217 - 60 +  
218 - _controller!.value.duration.inSeconds  
219 - .remainder(60);  
220 - int positionSecond = (totalSecond * degree).toInt();  
221 - _controller!  
222 - .seekTo(Duration(seconds: positionSecond));  
223 - },  
224 - ),  
225 - ),  
226 - Offstage(  
227 - offstage: _controller!.value.isPlaying,  
228 - child: IconButton(  
229 - onPressed: () {  
230 - _controller!.play();  
231 - },  
232 - icon: Image.asset(  
233 - 'video_stop'.assetPng,  
234 - width: 70.w,  
235 - height: 70.h,  
236 - ),  
237 - ),  
238 - )  
239 - ],  
240 - ) 193 + alignment: Alignment.center,
  194 + children: [
  195 + SizedBox(
  196 + height: double.infinity,
  197 + width: double.infinity,
  198 + child: AspectRatio(
  199 + aspectRatio: _controller!.value.aspectRatio,
  200 + child: VideoPlayer(_controller!),
  201 + ),
  202 + ),
  203 + Offstage(
  204 + offstage: _hiddenTipView,
  205 + child: VideoOperaWidget(
  206 + title: widget.typeTitle ?? 'song',
  207 + degree: _playDegree,
  208 + totalTime: _totalTime,
  209 + currentTime: _currentTime,
  210 + isPlay: _controller!.value.isPlaying,
  211 + actionEvent: (OperationType type) {
  212 + actionType(type);
  213 + },
  214 + sliderChangeEvent: (double degree) {
  215 + int totalSecond = _controller!
  216 + .value.duration.inMinutes
  217 + .remainder(60) *
  218 + 60 +
  219 + _controller!.value.duration.inSeconds
  220 + .remainder(60);
  221 + int positionSecond = (totalSecond * degree).toInt();
  222 + _controller!
  223 + .seekTo(Duration(seconds: positionSecond));
  224 + },
  225 + ),
  226 + ),
  227 + Offstage(
  228 + offstage: _controller!.value.isPlaying,
  229 + child: IconButton(
  230 + onPressed: () {
  231 + _controller!.play();
  232 + },
  233 + icon: Image.asset(
  234 + 'video_stop'.assetPng,
  235 + width: 70.w,
  236 + height: 70.h,
  237 + ),
  238 + ),
  239 + )
  240 + ],
  241 + )
241 : Container( 242 : Container(
242 - color: Colors.white,  
243 - child: const CircularProgressIndicator(),  
244 - // Text(  
245 - // '视频加载中....',  
246 - // style: TextStyle(fontSize: 20.sp, color: Colors.black),  
247 - // ),  
248 - ), 243 + color: Colors.white,
  244 + child: const CircularProgressIndicator(),
  245 + // Text(
  246 + // '视频加载中....',
  247 + // style: TextStyle(fontSize: 20.sp, color: Colors.black),
  248 + // ),
  249 + ),
249 ), 250 ),
250 ), 251 ),
251 ); 252 );