Commit 934e2b4731a8a7fc1da8ab39782f5ac7707a07a4

Authored by liangchengyou
1 parent a63404a0

feat:权限调整+课程进度接口对接

lib/common/dialogs/customer_dialog.dart
... ... @@ -20,12 +20,12 @@ class CustomerTwoActionDialog extends Dialog {
20 20 Widget build(BuildContext context) {
21 21 super.build(context);
22 22 return Container(
23   - height: 238.h,
  23 + height: 208.h,
24 24 alignment: Alignment.center,
25 25 child: ConstrainedBox(
26 26 constraints: BoxConstraints(
27   - maxHeight: 238.h,
28   - maxWidth: 407.w
  27 + maxHeight: 208.h,
  28 + maxWidth: 247.w
29 29 ),
30 30 child: Container(
31 31 padding: EdgeInsets.symmetric(horizontal: 24.w,vertical: 15.h),
... ... @@ -48,13 +48,16 @@ class CustomerTwoActionDialog extends Dialog {
48 48 ),
49 49 ),
50 50 Expanded(
51   - child: Text(
52   - content,
53   - textAlign: TextAlign.left,
54   - style: TextStyle(
55   - color: const Color(0xFF333333),
56   - fontSize: 13.sp
57   - )
  51 + child: Container(
  52 + alignment: Alignment.center,
  53 + child: Text(
  54 + content,
  55 + textAlign: TextAlign.left,
  56 + style: TextStyle(
  57 + color: const Color(0xFF333333),
  58 + fontSize: 13.sp
  59 + )
  60 + ),
58 61 ),
59 62 ),
60 63 Row(
... ... @@ -63,9 +66,8 @@ class CustomerTwoActionDialog extends Dialog {
63 66 TextButton(
64 67 onPressed: leftTap,
65 68 child: Container(
66   - height: 31.h,
67 69 padding: EdgeInsets.symmetric(
68   - horizontal: 6.w
  70 + horizontal: 6.w,vertical: 2.h
69 71 ),
70 72 decoration: BoxDecoration(
71 73 color: const Color(0xFFFBB661),
... ... @@ -87,7 +89,6 @@ class CustomerTwoActionDialog extends Dialog {
87 89 TextButton(
88 90 onPressed: rightTap,
89 91 child: Container(
90   - height: 31.h,
91 92 decoration: BoxDecoration(
92 93 color: Colors.white,
93 94 border: Border.all(
... ... @@ -97,8 +98,9 @@ class CustomerTwoActionDialog extends Dialog {
97 98 borderRadius: BorderRadius.circular(10.r)
98 99 ),
99 100 padding: EdgeInsets.symmetric(
100   - horizontal: 6.w
  101 + horizontal: 6.w,vertical: 2.h
101 102 ),
  103 + alignment: Alignment.center,
102 104 child: Text(
103 105 rightTitle,
104 106 textAlign: TextAlign.center,
... ... @@ -131,8 +133,8 @@ class CustomerOneActionDialog extends Dialog {
131 133 alignment: Alignment.center,
132 134 child: ConstrainedBox(
133 135 constraints: BoxConstraints(
134   - maxHeight: 238.h,
135   - maxWidth: 407.w
  136 + maxHeight: 208.h,
  137 + maxWidth: 247.w
136 138 ),
137 139 child: Container(
138 140 padding: EdgeInsets.symmetric(horizontal: 24.w,vertical: 15.h),
... ...
lib/common/request/apis.dart
... ... @@ -76,4 +76,10 @@ class Apis {
76 76  
77 77 /// 获取阿里云oss鉴权信息
78 78 static const String aliyunOssSts = 'oss/sts/upload';
  79 +
  80 + /// 进入课堂
  81 + static const String enterClass = 'course/enter/class';
  82 +
  83 + /// 退出课堂
  84 + static const String exitClass = 'course/exit/class';
79 85 }
... ...
lib/common/request/dao/listen_dao.dart
... ... @@ -35,4 +35,16 @@ class ListenDao {
35 35 var data = await requestClient.post(Apis.followResult,data: {'frequency':frequency,'videoFollowReadId':videoFollowReadId});
36 36 return data;
37 37 }
  38 +
  39 + ///进入课堂
  40 + static Future enterClass(courseLessonId) async {
  41 + var data = await requestClient.post(Apis.enterClass,data: {'courseLessonId':courseLessonId});
  42 + return data;
  43 + }
  44 +
  45 + ///退出课堂
  46 + static Future exitClass(courseLessonId,currentStep,currentTime) async {
  47 + var data = await requestClient.post(Apis.exitClass,data: {'courseLessonId':courseLessonId,'currentStep':currentStep,'currentTime':currentTime});
  48 + return data;
  49 + }
38 50 }
... ...
lib/common/request/request_client.dart
... ... @@ -45,10 +45,10 @@ class RequestClient {
45 45 data = _convertRequestData(data);
46 46  
47 47 Response response = await _dio.request(url, queryParameters: queryParameters, data: data, options: options);
48   - print("response.body type=${response.data.runtimeType}");
  48 + debugPrint("response.body type=${response.data.runtimeType}");
49 49 return _handleResponse<T>(response, onResponse);
50 50 } catch (e) {
51   - print("e type=${e.runtimeType}");
  51 + debugPrint("e type=${e.runtimeType}");
52 52 if (e is ApiException && e.code == 405) {
53 53 showToast('登录已失效,请重新登录!', duration: const Duration(seconds: 3));
54 54 UserUtil.logout();
... ... @@ -166,7 +166,7 @@ class RequestClient {
166 166 }
167 167 } else {
168 168 ApiException exception = ApiException(response.statusCode, ApiException.unknownException);
169   - print("_handleResponse exception type=${exception.runtimeType}");
  169 + debugPrint("_handleResponse exception type=${exception.runtimeType}");
170 170 throw exception;
171 171 }
172 172 }
... ... @@ -177,7 +177,7 @@ class RequestClient {
177 177 return response.data;
178 178 } else {
179 179 ApiException exception = ApiException(response.code, response.msg);
180   - print("_handleBusinessResponse exception type=${exception.runtimeType}");
  180 + debugPrint("_handleBusinessResponse exception type=${exception.runtimeType}");
181 181 throw exception;
182 182 }
183 183 }
... ...
lib/common/request/token_interceptor.dart
1 1 import 'package:dio/dio.dart';
  2 +import 'package:package_info_plus/package_info_plus.dart';
2 3 import 'package:wow_english/common/core/user_util.dart';
  4 +import 'package:wow_english/common/request/basic_config.dart';
3 5  
4 6 class TokenInterceptor extends Interceptor {
5 7 @override
... ...
lib/pages/home/bloc/home_bloc.dart
... ... @@ -26,6 +26,8 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; {
26 26  
27 27 HomeBloc(this.moduleId) : super(HomeInitial()) {
28 28 on<RequestDataEvent>(_requestData);
  29 + on<RequestExitClassEvent>(_requestExitClass);
  30 + on<RequestEnterClassEvent>(_requestEnterClass);
29 31 on<RequestVideoLessonEvent>(_requestVideoLesson);
30 32 }
31 33  
... ... @@ -46,7 +48,7 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; {
46 48 try {
47 49 await loading(() async {
48 50 _processEntity = await ListenDao.process(event.courseLessonId);
49   - emitter(RequestVideoLessonState(event.courseType));
  51 + emitter(RequestVideoLessonState(event.courseLessonId,event.courseType));
50 52 });
51 53 } catch (e) {
52 54 if (e is ApiException) {
... ... @@ -54,4 +56,22 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; {
54 56 }
55 57 }
56 58 }
  59 +
  60 +
  61 + void _requestEnterClass(RequestEnterClassEvent event,Emitter<HomeState> emitter) async {
  62 + try {
  63 + await loading(() async {
  64 + await ListenDao.enterClass(event.courseLessonId);
  65 + emitter(RequestEnterClassState(event.courseLessonId,event.courseType));
  66 + });
  67 + } catch (e) {
  68 + if (e is ApiException) {
  69 + showToast(e.message??'请求失败,请检查网络连接');
  70 + }
  71 + }
  72 + }
  73 +
  74 + void _requestExitClass(RequestExitClassEvent event,Emitter<HomeState> emitter) async {
  75 + await ListenDao.exitClass(event.courseLessonId,event.currentStep,event.currentTime);
  76 + }
57 77 }
... ...
lib/pages/home/bloc/home_event.dart
... ... @@ -11,3 +11,18 @@ class RequestVideoLessonEvent extends HomeEvent {
11 11 final int courseType;
12 12 RequestVideoLessonEvent(this.courseLessonId, this.courseType);
13 13 }
  14 +
  15 +///进入课堂
  16 +class RequestEnterClassEvent extends HomeEvent {
  17 + final String courseLessonId;
  18 + final int courseType;
  19 + RequestEnterClassEvent(this.courseLessonId,this.courseType);
  20 +}
  21 +
  22 +///退出课堂
  23 +class RequestExitClassEvent extends HomeEvent {
  24 + final String courseLessonId;
  25 + final String currentStep;
  26 + final String currentTime;
  27 + RequestExitClassEvent(this.courseLessonId,this.currentStep,this.currentTime);
  28 +}
... ...
lib/pages/home/bloc/home_state.dart
... ... @@ -8,6 +8,13 @@ class HomeInitial extends HomeState {}
8 8 class HomeDataLoadState extends HomeState {}
9 9  
10 10 class RequestVideoLessonState extends HomeState {
  11 + final String courseLessonId;
11 12 final int type;
12   - RequestVideoLessonState(this.type);
  13 + RequestVideoLessonState(this.courseLessonId,this.type);
  14 +}
  15 +
  16 +class RequestEnterClassState extends HomeState{
  17 + final String courseLessonId;
  18 + final int courseType;
  19 + RequestEnterClassState(this.courseLessonId,this.courseType);
13 20 }
... ...
lib/pages/home/home_page.dart
... ... @@ -73,10 +73,56 @@ class _HomePageView extends StatelessWidget {
73 73 title = 'bonus';
74 74 }
75 75  
76   - if (videoUrl.isEmpty && !videoUrl.contains('http')) {
  76 + if (videoUrl.isEmpty || !videoUrl.contains('http')) {
  77 + return;
  78 + }
  79 + pushNamed(AppRouteName.lookVideo,arguments: {'videoUrl':videoUrl,'title':title,'courseLessonId':state.courseLessonId}).then((value) {
  80 + if (value != null) {
  81 + Map<String,String> dataMap = value as Map<String,String>;
  82 + bloc.add(RequestExitClassEvent(
  83 + dataMap['courseLessonId']!,
  84 + '0',
  85 + dataMap['currentTime']!,
  86 + ));
  87 + }
  88 + });
  89 + return;
  90 + }
  91 +
  92 + if (state is RequestEnterClassState) {
  93 + if (state.courseType != 3 && state.courseType != 4) {///视频类型
  94 + ///获取视频课程内容
  95 + bloc.add(RequestVideoLessonEvent(state.courseLessonId,state.courseType));
  96 + return;
  97 + }
  98 +
  99 + if (state.courseType == 4) {//绘本
  100 + pushNamed(AppRouteName.reading, arguments: {'courseLessonId':state.courseLessonId}).then((value) {
  101 + if (value != null) {
  102 + Map<String,String> dataMap = value as Map<String,String>;
  103 + bloc.add(RequestExitClassEvent(
  104 + dataMap['courseLessonId']!,
  105 + dataMap['currentStep']!,
  106 + '0'
  107 + ));
  108 + }
  109 + });
  110 + return;
  111 + }
  112 +
  113 + if (state.courseType == 3) {//练习
  114 + pushNamed(AppRouteName.topicPic,arguments: {'courseLessonId':state.courseLessonId}).then((value) {
  115 + if (value != null) {
  116 + Map<String,String> dataMap = value as Map<String,String>;
  117 + bloc.add(RequestExitClassEvent(
  118 + dataMap['courseLessonId']!,
  119 + dataMap['currentStep']!,
  120 + '0'
  121 + ));
  122 + }
  123 + });
77 124 return;
78 125 }
79   - pushNamed(AppRouteName.lookVideo,arguments: {'videoUrl':videoUrl,'title':title});
80 126 }
81 127 },
82 128 child: _homeView(),
... ... @@ -116,7 +162,8 @@ class _HomePageView extends StatelessWidget {
116 162 showToast('当前课程暂未解锁');
117 163 return;
118 164 }
119   - bloc.add(RequestVideoLessonEvent(data.id!,data.courseType!));
  165 + ///进入课堂
  166 + bloc.add(RequestEnterClassEvent(data.id!,data.courseType!));
120 167 },
121 168 child: HomeBoundsItem(
122 169 imageUrl: data?.coverUrl,
... ... @@ -133,18 +180,8 @@ class _HomePageView extends StatelessWidget {
133 180 showToast('当前课程暂未解锁');
134 181 return;
135 182 }
136   - if (data.courseType == 4) {//绘本
137   - Navigator.of(context).pushNamed(AppRouteName.reading, arguments: {'courseLessonId':data.id!});
138   - return;
139   - }
140   -
141   - if (data.courseType == 3) {//练习
142   - Navigator.of(context).pushNamed(AppRouteName.topicPic,arguments: {'courseLessonId':data.id!});
143   - return;
144   - }
145   -
146   - //儿歌/看视频
147   - bloc.add(RequestVideoLessonEvent(data.id!,data.courseType!));
  183 + ///进入课堂
  184 + bloc.add(RequestEnterClassEvent(data.id!,data.courseType!));
148 185 },
149 186 child: HomeVideoItem(
150 187 themColor: bloc.modelData?.courseModuleThemeColor,
... ...
lib/pages/practice/topic_picture_page.dart
... ... @@ -6,6 +6,7 @@ import &#39;package:wow_english/common/core/user_util.dart&#39;;
6 6 import 'package:wow_english/common/extension/string_extension.dart';
7 7 import 'package:wow_english/common/widgets/ow_image_widget.dart';
8 8 import 'package:wow_english/models/course_process_entity.dart';
  9 +import 'package:wow_english/route/route.dart';
9 10 import 'package:wow_english/utils/toast_util.dart';
10 11  
11 12 import 'bloc/topic_picture_bloc.dart';
... ... @@ -46,7 +47,6 @@ class _TopicPicturePage extends StatelessWidget {
46 47 if (state is RequestDataState) {
47 48 context.read<TopicPictureBloc>().add(CurrentPageIndexChangeEvent(0));
48 49 }
49   -
50 50 if (state is XSVoiceTestState) {
51 51  
52 52 }
... ... @@ -67,7 +67,12 @@ class _TopicPicturePage extends StatelessWidget {
67 67 PracticeHeaderWidget(
68 68 title: '${bloc.currentPage}/${bloc.entity?.topics?.length}',
69 69 onTap: () {
70   - Navigator.pop(context);
  70 + popPage(
  71 + data:{
  72 + 'currentStep':bloc.currentPage.toString(),
  73 + 'courseLessonId':(int.parse(bloc.courseLessonId)+1).toString()
  74 + });
  75 + // Navigator.pop(context);
71 76 },
72 77 ),
73 78 Expanded(
... ...
lib/pages/repeataftercontent/bloc/repeat_after_content_bloc.dart
... ... @@ -9,6 +9,8 @@ import &#39;package:flutter_sound/flutter_sound.dart&#39;;
9 9 import 'package:path_provider/path_provider.dart';
10 10 import 'package:permission_handler/permission_handler.dart';
11 11 import 'package:wow_english/common/request/dao/listen_dao.dart';
  12 +import 'package:wow_english/route/route.dart';
  13 +import '../../../common/dialogs/show_dialog.dart';
12 14 import '../../../common/request/exception.dart';
13 15 import '../../../models/read_content_entity.dart';
14 16 import '../../../utils/loading.dart';
... ... @@ -80,7 +82,7 @@ class RepeatAfterContentBloc extends Bloc&lt;RepeatAfterContentEvent, RepeatAfterCo
80 82 ///录音
81 83 late FlutterSoundRecorder _soundRecorder;
82 84 late FlutterSoundPlayer _soundPlayer;
83   - StreamSubscription? _soundPlayerListen;
  85 + // StreamSubscription? _soundPlayerListen;
84 86  
85 87 RepeatAfterContentBloc(this.courseLessonId) : super(RepeatAfterContentInitial()) {
86 88 on<VoiceRecordStateChangeEvent>(_voiceRecordStateChange);
... ... @@ -123,13 +125,14 @@ class RepeatAfterContentBloc extends Bloc&lt;RepeatAfterContentEvent, RepeatAfterCo
123 125  
124 126 //录音
125 127 _soundRecorder = FlutterSoundRecorder();
126   -
127   -
128   - await _soundRecorder.openRecorder();
129   - // await _soundRecorder.setSubscriptionDuration(const Duration(milliseconds: 10));
130   -
131 128 //音屏
132 129 _soundPlayer = FlutterSoundPlayer();
  130 + _init();
  131 + }
  132 +
  133 + void _init() async {
  134 + await _soundRecorder.openRecorder();
  135 + await _soundRecorder.setSubscriptionDuration(const Duration(milliseconds: 10));
133 136 //设置音频
134 137 final session = await AudioSession.instance;
135 138 await session.configure(AudioSessionConfiguration(
... ... @@ -151,7 +154,7 @@ class RepeatAfterContentBloc extends Bloc&lt;RepeatAfterContentEvent, RepeatAfterCo
151 154 ));
152 155 await _soundPlayer.closePlayer();
153 156 await _soundPlayer.openPlayer();
154   - // await _soundPlayer.setSubscriptionDuration(const Duration(milliseconds: 10));
  157 + await _soundPlayer.setSubscriptionDuration(const Duration(milliseconds: 10));
155 158 }
156 159  
157 160 ///请求数据
... ... @@ -171,9 +174,7 @@ class RepeatAfterContentBloc extends Bloc&lt;RepeatAfterContentEvent, RepeatAfterCo
171 174 ///跟读结果
172 175 void _postFollowReadContent(PostFollowReadContentEvent event,Emitter<RepeatAfterContentState> emitter) async {
173 176 try {
174   - await loading(() async {
175   - _entityList = await ListenDao.followResult(_recordNumber.toString(),courseLessonId);
176   - });
  177 + await ListenDao.followResult(_recordNumber.toString(),courseLessonId);
177 178 } catch (e) {
178 179 if (e is ApiException) {
179 180  
... ... @@ -203,6 +204,9 @@ class RepeatAfterContentBloc extends Bloc&lt;RepeatAfterContentEvent, RepeatAfterCo
203 204  
204 205 ///先声测试
205 206 void _voiceXsTest(XSVoiceTestEvent event,Emitter<RepeatAfterContentState> emitter) async {
  207 + _recordNumber += 1;
  208 + _xSCheckState = XSVoiceCheckState.start;
  209 + emitter(XSVoiceTestState());
206 210 await methodChannel.invokeMethod(
207 211 'startLocalVoice',
208 212 {
... ... @@ -212,9 +216,6 @@ class RepeatAfterContentBloc extends Bloc&lt;RepeatAfterContentEvent, RepeatAfterCo
212 216 'userId':event.userId.toString()
213 217 }
214 218 );
215   - _recordNumber++;
216   - _xSCheckState = XSVoiceCheckState.start;
217   - emitter(XSVoiceTestState());
218 219 }
219 220  
220 221 ///终止评测
... ... @@ -246,8 +247,7 @@ class RepeatAfterContentBloc extends Bloc&lt;RepeatAfterContentEvent, RepeatAfterCo
246 247  
247 248 await _soundPlayer.startPlayer(
248 249 fromURI: path,
249   - codec: Codec.aacADTS,
250   - sampleRate: 44000,
  250 + codec: Codec.pcm16WAV,
251 251 whenFinished: (){
252 252  
253 253 }
... ... @@ -292,6 +292,7 @@ class RepeatAfterContentBloc extends Bloc&lt;RepeatAfterContentEvent, RepeatAfterCo
292 292 );
293 293 debugPrint('=====> 开始录音');
294 294 _voiceRecordState = VoiceRecordState.voiceRecording;
  295 + _xSCheckState = XSVoiceCheckState.unKnow;
295 296 emitter(VoiceRecordStateChange());
296 297 });
297 298 } catch (error) {
... ... @@ -321,7 +322,7 @@ class RepeatAfterContentBloc extends Bloc&lt;RepeatAfterContentEvent, RepeatAfterCo
321 322 } else if (status.isDenied) {
322 323 requestPermission(permission);
323 324 } else if (status.isPermanentlyDenied) {
324   - openAppSettings();
  325 + showDialog();
325 326 } else if (status.isRestricted) {
326 327 requestPermission(permission);
327 328 } else {
... ... @@ -339,7 +340,16 @@ class RepeatAfterContentBloc extends Bloc&lt;RepeatAfterContentEvent, RepeatAfterCo
339 340 void requestPermission(Permission permission) async {
340 341 PermissionStatus status = await permission.request();
341 342 if (status.isPermanentlyDenied) {
342   - openAppSettings();
  343 + showDialog();
343 344 }
344 345 }
  346 +
  347 + void showDialog() async {
  348 + showTwoActionDialog('提示', '取消', '去设置', '请进入设置页打开麦克风权限', () {
  349 + popPage();
  350 + }, () {
  351 + popPage();
  352 + openAppSettings();
  353 + });
  354 + }
345 355 }
... ...
lib/pages/repeataftercontent/widgets/repeat_video_widget.dart
... ... @@ -152,17 +152,20 @@ class _RepeatVideoWidgetState extends State&lt;RepeatVideoWidget&gt; {
152 152 }
153 153  
154 154 void _initVideo(String videoUrl) {
155   - _controller = VideoPlayerController.network(widget.videoUrl??'')
156   - ..initialize().then((_){
157   - setState(() {
158   - _currentTime = formatDuration(_controller!.value.position);
159   - _totalTime = formatDuration(_controller!.value.duration);
160   - _controller!.setLooping(false);
161   - _controller!.setVolume(100);
162   - _controller!.play();
  155 + if(videoUrl.isNotEmpty) {
  156 + Uri uri = Uri.parse(videoUrl);
  157 + _controller = VideoPlayerController.networkUrl(uri)
  158 + ..initialize().then((_){
  159 + setState(() {
  160 + _currentTime = formatDuration(_controller!.value.position);
  161 + _totalTime = formatDuration(_controller!.value.duration);
  162 + _controller!.setLooping(false);
  163 + _controller!.setVolume(100);
  164 + _controller!.play();
  165 + });
  166 + _addListener();
163 167 });
164   - _addListener();
165   - });
  168 + }
166 169 }
167 170  
168 171 void _destroyVideo() {
... ...
lib/pages/user/modify/user_avatar_bloc/user_avatar_bloc.dart
... ... @@ -3,12 +3,12 @@ import &#39;dart:io&#39;;
3 3 import 'package:device_info_plus/device_info_plus.dart';
4 4 import 'package:flutter/cupertino.dart';
5 5 import 'package:flutter_bloc/flutter_bloc.dart';
6   -import 'package:flutter_easyloading/flutter_easyloading.dart';
7 6 import 'package:image_picker/image_picker.dart';
8 7 import 'package:permission_handler/permission_handler.dart';
9 8 import 'package:wow_english/common/core/assets_const.dart';
10 9 import 'package:wow_english/common/core/user_util.dart';
11   -import 'package:wow_english/common/request/dao/user_dao.dart';
  10 +import 'package:wow_english/common/dialogs/show_dialog.dart';
  11 +import 'package:wow_english/route/route.dart';
12 12 import 'package:wow_english/utils/aliyun_oss_util.dart';
13 13 import 'package:wow_english/utils/log_util.dart';
14 14  
... ... @@ -46,7 +46,18 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; {
46 46 }
47 47  
48 48 void _getImageFromPhoto(GetImageFromPhotoEvent event, Emitter<UserAvatarState> emitter) async {
49   - await getPhotoPermissionStatus().then((value) async {
  49 + Permission permission;
  50 + if (Platform.isAndroid) {
  51 + final androidInfo = await DeviceInfoPlugin().androidInfo;
  52 + if (androidInfo.version.sdkInt <= 32) {
  53 + permission = Permission.storage;
  54 + } else {
  55 + permission = Permission.photos;
  56 + }
  57 + } else {
  58 + permission = Permission.photos;
  59 + }
  60 + await getPermissionStatus(permission).then((value) async {
50 61 if (!value) {
51 62 debugPrint('失败$value');
52 63 return;
... ... @@ -62,7 +73,7 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; {
62 73 }
63 74  
64 75 void _getImageFromCamera(GetImageFromCameraEvent event, Emitter<UserAvatarState> emitter) async {
65   - await getCameraPermissionStatus().then((value) async {
  76 + await getPermissionStatus(Permission.camera).then((value) async {
66 77 if (!value) {
67 78 debugPrint('失败$value');
68 79 return;
... ... @@ -83,46 +94,19 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; {
83 94 emitter(ChangeUserEnterAppState());
84 95 }
85 96  
86   - ///获取相机权限
87   - Future<bool> getCameraPermissionStatus() async {
88   - Permission permission = Permission.camera;
89   - //granted 通过,denied 被拒绝,permanentlyDenied 拒绝且不在提示
  97 + Future<bool> getPermissionStatus(Permission permission) async {
90 98 PermissionStatus status = await permission.status;
91 99 if (status.isGranted) {
92 100 return true;
93 101 } else if (status.isDenied) {
94 102 _requestPermission(permission);
95 103 } else if (status.isPermanentlyDenied) {
96   - openAppSettings();
  104 + showDialog(permission);
97 105 } else if (status.isRestricted) {
98 106 _requestPermission(permission);
99   - } else {}
100   - return false;
101   - }
102   -
103   - ///获取相册权限
104   - Future<bool> getPhotoPermissionStatus() async {
105   - Permission permission;
106   - if (Platform.isAndroid) {
107   - final androidInfo = await DeviceInfoPlugin().androidInfo;
108   - if (androidInfo.version.sdkInt <= 32) {
109   - permission = Permission.storage;
110   - } else {
111   - permission = Permission.photos;
112   - }
113 107 } else {
114   - permission = Permission.photos;
  108 +
115 109 }
116   - PermissionStatus status = await permission.status;
117   - if (status.isGranted) {
118   - return true;
119   - } else if (status.isDenied) {
120   - _requestPermission(permission);
121   - } else if (status.isPermanentlyDenied) {
122   - openAppSettings();
123   - } else if (status.isRestricted) {
124   - _requestPermission(permission);
125   - } else {}
126 110 return false;
127 111 }
128 112  
... ... @@ -130,7 +114,17 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; {
130 114 void _requestPermission(Permission permission) async {
131 115 PermissionStatus status = await permission.request();
132 116 if (status.isPermanentlyDenied) {
133   - openAppSettings();
  117 + showDialog(permission);
134 118 }
135 119 }
  120 +
  121 + void showDialog(Permission permission) {
  122 + final contentStr = permission == Permission.camera ? '相机权限未开启,请到设置中打开' : '相册权限未开启,请到设置中打开';
  123 + showTwoActionDialog('提示', '取消', '去设置', contentStr, () {
  124 + popPage();
  125 + }, () {
  126 + popPage();
  127 + openAppSettings();
  128 + });
  129 + }
136 130 }
... ...
lib/pages/video/lookvideo/look_video_page.dart
... ... @@ -2,10 +2,11 @@ import &#39;package:flutter/material.dart&#39;;
2 2 import 'package:wow_english/pages/video/lookvideo/widgets/video_widget.dart';
3 3  
4 4 class LookVideoPage extends StatefulWidget {
5   - const LookVideoPage({super.key, this.videoUrl, this.typeTitle});
  5 + const LookVideoPage({super.key, this.videoUrl, this.typeTitle, this.courseLessonId});
6 6  
7 7 final String? videoUrl;
8 8 final String? typeTitle;
  9 + final String? courseLessonId;
9 10  
10 11 @override
11 12 State<StatefulWidget> createState() {
... ... @@ -21,6 +22,7 @@ class _LookVideoPageState extends State&lt;LookVideoPage&gt; {
21 22 child: VideoWidget(
22 23 videoUrl: widget.videoUrl??'',
23 24 typeTitle: widget.typeTitle,
  25 + courseLessonId: widget.courseLessonId??'',
24 26 ),
25 27 );
26 28 }
... ...
lib/pages/video/lookvideo/widgets/video_widget.dart
... ... @@ -3,14 +3,16 @@ import &#39;package:flutter/material.dart&#39;;
3 3 import 'package:flutter_screenutil/flutter_screenutil.dart';
4 4 import 'package:video_player/video_player.dart';
5 5 import 'package:wow_english/common/extension/string_extension.dart';
  6 +import 'package:wow_english/route/route.dart';
6 7  
7 8 import 'video_opera_widget.dart';
8 9  
9 10 class VideoWidget extends StatefulWidget {
10   - const VideoWidget({super.key, this.videoUrl = '',this.typeTitle});
  11 + const VideoWidget({super.key, this.videoUrl = '',this.typeTitle, this.courseLessonId = ''});
11 12  
12 13 final String videoUrl;
13 14 final String? typeTitle;
  15 + final String courseLessonId;
14 16  
15 17 @override
16 18 State<StatefulWidget> createState() {
... ... @@ -74,7 +76,16 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; {
74 76  
75 77 void actionType(OperationType type) async {
76 78 if (type == OperationType.back) {
77   - Navigator.pop(context);
  79 + if (widget.courseLessonId.isEmpty) {
  80 + popPage();
  81 + } else {
  82 + if (_controller == null) {
  83 + popPage();
  84 + return;
  85 + }
  86 + String currentTime = (_controller!.value.position.inMinutes.remainder(60)*60+_controller!.value.position.inSeconds.remainder(60)).toString();
  87 + popPage(data:{'courseLessonId':widget.courseLessonId,'currentTime':currentTime});
  88 + }
78 89 } else if (type == OperationType.playState) {
79 90 if (_controller!.value.isPlaying) {
80 91 _controller!.pause();
... ... @@ -90,7 +101,8 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; {
90 101 @override
91 102 void initState() {
92 103 super.initState();
93   - _controller = VideoPlayerController.network(widget.videoUrl)
  104 + Uri uri = Uri.parse(widget.videoUrl);
  105 + _controller = VideoPlayerController.networkUrl(uri)
94 106 ..initialize().then((_){
95 107 startTimer();
96 108 setState(() {
... ...
lib/route/route.dart
... ... @@ -107,7 +107,7 @@ class AppRouter {
107 107 case AppRouteName.userAvatar:
108 108 var pageType = 0;
109 109 if (settings.arguments != null) {
110   - final content = (settings.arguments as Map)['pageType'] as String ?? '0';
  110 + final content = (settings.arguments as Map)['pageType'] as String;
111 111 pageType = int.parse(content);
112 112 }
113 113 return CupertinoPageRoute(
... ... @@ -125,17 +125,19 @@ class AppRouter {
125 125 case AppRouteName.topicPic:
126 126 var courseLessonId = '';
127 127 if (settings.arguments != null) {
128   - courseLessonId = (settings.arguments as Map)['courseLessonId'] as String ?? '';
  128 + courseLessonId = (settings.arguments as Map)['courseLessonId'] as String;
129 129 }
130 130 return CupertinoPageRoute(builder: (_) => TopicPicturePage(courseLessonId: courseLessonId));
131 131 case AppRouteName.lookVideo:
132 132 final videoUrl = (settings.arguments as Map)['videoUrl'] as String;
133 133 final title = (settings.arguments as Map)['title'] as String?;
  134 + final courseLessonId = (settings.arguments as Map)['courseLessonId'] as String?;
134 135 return CupertinoPageRoute(
135 136 builder: (_) => LookVideoPage(
136   - videoUrl: videoUrl,
137   - typeTitle: title,
138   - ));
  137 + videoUrl: videoUrl,
  138 + typeTitle: title,
  139 + courseLessonId: courseLessonId,
  140 + ));
139 141 /*case AppRouteName.setPwd:
140 142 case AppRouteName.setPwd:
141 143 phoneNum: phoneNum,
... ... @@ -153,7 +155,7 @@ class AppRouter {
153 155 case AppRouteName.readAfterContent:
154 156 var videoFollowReadId = '';
155 157 if (settings.arguments != null) {
156   - videoFollowReadId = (settings.arguments as Map)['videoFollowReadId'] as String ?? '';
  158 + videoFollowReadId = (settings.arguments as Map)['videoFollowReadId'] as String;
157 159 }
158 160 return CupertinoPageRoute(builder: (_) => RepeatAfterContentPage(videoFollowReadId: videoFollowReadId));
159 161 case AppRouteName.tab:
... ... @@ -166,7 +168,7 @@ class AppRouter {
166 168 case AppRouteName.reading:
167 169 var courseLessonId = '';
168 170 if (settings.arguments != null) {
169   - courseLessonId = (settings.arguments as Map)['courseLessonId'] as String ?? '';
  171 + courseLessonId = (settings.arguments as Map)['courseLessonId'] as String;
170 172 }
171 173 return CupertinoPageRoute(builder: (_) => ReadingPage(courseLessonId: courseLessonId));
172 174 default:
... ... @@ -176,14 +178,16 @@ class AppRouter {
176 178 }
177 179 }
178 180  
179   -void pushNamed(String routeName, {Object? arguments}) {
180   - Navigator.of(AppRouter.context).pushNamed(routeName, arguments: arguments);
  181 +Future pushNamed(String routeName, {Object? arguments}) {
  182 + return Navigator.of(AppRouter.context).pushNamed(routeName, arguments: arguments).then((value) {
  183 + return value;
  184 + });
181 185 }
182 186  
183   -void pushNamedAndRemoveUntil(String routeName, RoutePredicate predicate, {Object? arguments}) {
184   - Navigator.of(AppRouter.context).pushNamedAndRemoveUntil(routeName, predicate, arguments: arguments);
  187 +Future pushNamedAndRemoveUntil(String routeName, RoutePredicate predicate, {Object? arguments}) {
  188 + return Navigator.of(AppRouter.context).pushNamedAndRemoveUntil(routeName, predicate, arguments: arguments);
185 189 }
186 190  
187   -void popPage() {
188   - Navigator.pop(AppRouter.context);
  191 +void popPage({dynamic data}) {
  192 + Navigator.of(AppRouter.context).pop(data);
189 193 }
... ...
pubspec.yaml
... ... @@ -101,6 +101,8 @@ dependencies:
101 101 path_provider: ^2.0.15
102 102 # 阿里云oss https://pub.dev/packages/flutter_oss_aliyun
103 103 flutter_oss_aliyun: ^6.2.7
  104 + # App信息 https://pub.dev/packages/package_info_plus
  105 + package_info_plus: ^4.0.2
104 106  
105 107 dev_dependencies:
106 108 build_runner: ^2.4.4
... ...