Commit cb38bc9038f9948d1bae383e49a69b95f5a34875

Authored by liangchengyou
1 parent a38a01d4

feat:视频跟读逻辑处理

assets/images/and_book.png 0 → 100644

79.1 KB

assets/images/light_ground.png 0 → 100644

70.2 KB

assets/images/next.png 0 → 100644

1.35 KB

assets/images/previous.png 0 → 100644

1.37 KB

assets/images/title_ground.png 0 → 100644

32.8 KB

assets/images/video_background.png 0 → 100644

139 KB

assets/images/video_pause.png 0 → 100644

9.81 KB

assets/images/video_record.png 0 → 100644

7.97 KB

assets/images/voice_record_play.png 0 → 100644

4.68 KB

ios/Runner/XSMessageMehtodChannel.swift
... ... @@ -97,11 +97,8 @@ class XSMessageMehtodChannel: NSObject,SSOralEvaluatingManagerDelegate {
97 97 评测完成后的结果
98 98 */
99 99 func oralEvaluatingDidEnd(withResult result: [AnyHashable : Any]?, requestId request_id: String?) {
100   - print("评测完成结果")
101   - let resultDict:Dictionary<String, Any> = result?["result"] as! Dictionary
102   - resultData!["result"] = "1"
103   - //分数
104   - resultData!["overall"] = resultDict["overall"]
  100 + var resultDict:Dictionary<String, Any> = result as! Dictionary
  101 + resultData! = resultDict;
105 102 self.evaluateResult()
106 103 }
107 104  
... ... @@ -109,7 +106,6 @@ class XSMessageMehtodChannel: NSObject,SSOralEvaluatingManagerDelegate {
109 106 评测失败回调
110 107 */
111 108 func oralEvaluatingDidEndError(_ error: Error?, requestId request_id: String?) {
112   - print("评测失败")
113 109 messageChannel!.invokeMethod("voiceFail", arguments: error?.localizedDescription)
114 110 }
115 111  
... ... @@ -117,19 +113,13 @@ class XSMessageMehtodChannel: NSObject,SSOralEvaluatingManagerDelegate {
117 113 VAD(前置时间)超时回调
118 114 */
119 115 func oralEvaluatingDidVADFrontTimeOut() {
120   - print("前置超时--->取消")
121 116 SSOralEvaluatingManager.share().cancelEvaluate()
122   - if(resultData?.keys.count == 0) {
123   - resultData!["result"] = "0"
124   - self.evaluateResult();
125   - }
126 117 }
127 118  
128 119 /**
129 120 VAD(后置时间)超时回调
130 121 */
131 122 func oralEvaluatingDidVADBackTimeOut() {
132   - print("后置超时--->结束")
133 123 ///结束回调
134 124 SSOralEvaluatingManager.share().stopEvaluate();
135 125 }
... ...
lib/common/request/dao/listen_dao.dart
... ... @@ -3,6 +3,8 @@ import &#39;package:wow_english/models/course_process_entity.dart&#39;;
3 3 import 'package:wow_english/models/follow_read_entity.dart';
4 4 import 'package:wow_english/models/listen_entity.dart';
5 5  
  6 +import '../../../models/read_content_entity.dart';
  7 +
6 8 class ListenDao {
7 9 /// 磨耳朵
8 10 static Future<List<ListenEntity?>?> listen() async {
... ... @@ -21,4 +23,16 @@ class ListenDao {
21 23 var data = await requestClient.get<CourseProcessEntity>(Apis.process,queryParameters: {'courseLessonId':courseLessonId});
22 24 return data;
23 25 }
  26 +
  27 + ///获取视频跟读内容
  28 + static Future<List<ReadContentEntity?>?> readContent(videoFollowReadId) async {
  29 + var data = await requestClient.get<List<ReadContentEntity?>>(Apis.readContent,queryParameters: {'videoFollowReadId':videoFollowReadId});
  30 + return data;
  31 + }
  32 +
  33 + ///视频跟读提交结果
  34 + static Future followResult(frequency,videoFollowReadId) async {
  35 + var data = await requestClient.post(Apis.followResult,data: {'frequency':frequency,'videoFollowReadId':videoFollowReadId});
  36 + return data;
  37 + }
24 38 }
... ...
lib/common/widgets/textfield_customer_widget.dart
... ... @@ -48,9 +48,9 @@ class _TextFieldCustomerWidgetState extends State&lt;TextFieldCustomerWidget&gt; {
48 48 alignment: Alignment.center,
49 49 decoration: BoxDecoration(
50 50 image: DecorationImage(
51   - image: AssetImage('${widget.bgImageName}'.assetPng),
52   - fit: BoxFit.fill,
53   - )),
  51 + image: AssetImage('${widget.bgImageName}'.assetPng),
  52 + fit: BoxFit.fill,
  53 + )),
54 54 child: TextField(
55 55 inputFormatters: widget.inputFormatters,
56 56 controller: widget.controller,
... ...
lib/common/widgets/we_app_bar.dart
... ... @@ -35,9 +35,7 @@ class WEAppBar extends StatelessWidget implements PreferredSizeWidget {
35 35 ),
36 36 leading: leading ??
37 37 GestureDetector(
38   - onTap: () {
39   - Navigator.pop(context);
40   - },
  38 + onTap: () => onBack??Navigator.pop(context),
41 39 child: Container(
42 40 alignment: Alignment.center,
43 41 child: Image.asset(
... ...
lib/generated/json/base/json_convert_content.dart
... ... @@ -9,6 +9,7 @@ import &#39;package:wow_english/models/course_module_entity.dart&#39;;
9 9 import 'package:wow_english/models/course_process_entity.dart';
10 10 import 'package:wow_english/models/follow_read_entity.dart';
11 11 import 'package:wow_english/models/listen_entity.dart';
  12 +import 'package:wow_english/models/read_content_entity.dart';
12 13 import 'package:wow_english/models/user_entity.dart';
13 14  
14 15 JsonConvert jsonConvert = JsonConvert();
... ... @@ -27,6 +28,7 @@ class JsonConvert {
27 28 (CourseProcessVideos).toString(): CourseProcessVideos.fromJson,
28 29 (FollowReadEntity).toString(): FollowReadEntity.fromJson,
29 30 (ListenEntity).toString(): ListenEntity.fromJson,
  31 + (ReadContentEntity).toString(): ReadContentEntity.fromJson,
30 32 (UserEntity).toString(): UserEntity.fromJson,
31 33 };
32 34  
... ... @@ -136,6 +138,9 @@ List&lt;T&gt;? convertListNotNull&lt;T&gt;(dynamic value, {EnumConvertFunction? enumConvert}
136 138 if(<ListenEntity>[] is M){
137 139 return data.map<ListenEntity>((Map<String, dynamic> e) => ListenEntity.fromJson(e)).toList() as M;
138 140 }
  141 + if(<ReadContentEntity>[] is M){
  142 + return data.map<ReadContentEntity>((Map<String, dynamic> e) => ReadContentEntity.fromJson(e)).toList() as M;
  143 + }
139 144 if(<UserEntity>[] is M){
140 145 return data.map<UserEntity>((Map<String, dynamic> e) => UserEntity.fromJson(e)).toList() as M;
141 146 }
... ...
lib/generated/json/course_process_entity.g.dart
... ... @@ -78,6 +78,14 @@ CourseProcessReadings $CourseProcessReadingsFromJson(Map&lt;String, dynamic&gt; json)
78 78 if (word != null) {
79 79 courseProcessReadings.word = word;
80 80 }
  81 + final String? recordUrl = jsonConvert.convert<String>(json['recordUrl']);
  82 + if (recordUrl != null) {
  83 + courseProcessReadings.recordUrl = recordUrl;
  84 + }
  85 + final String? recordScore = jsonConvert.convert<String>(json['recordScore']);
  86 + if (recordScore != null) {
  87 + courseProcessReadings.recordScore = recordScore;
  88 + }
81 89 return courseProcessReadings;
82 90 }
83 91  
... ... @@ -93,6 +101,8 @@ Map&lt;String, dynamic&gt; $CourseProcessReadingsToJson(CourseProcessReadings entity)
93 101 data['picUrl'] = entity.picUrl;
94 102 data['sortOrder'] = entity.sortOrder;
95 103 data['word'] = entity.word;
  104 + data['recordUrl'] = entity.recordUrl;
  105 + data['recordScore'] = entity.recordScore;
96 106 return data;
97 107 }
98 108  
... ...
lib/generated/json/read_content_entity.g.dart 0 → 100644
  1 +import 'package:wow_english/generated/json/base/json_convert_content.dart';
  2 +import 'package:wow_english/models/read_content_entity.dart';
  3 +
  4 +ReadContentEntity $ReadContentEntityFromJson(Map<String, dynamic> json) {
  5 + final ReadContentEntity readContentEntity = ReadContentEntity();
  6 + final String? createTime = jsonConvert.convert<String>(json['createTime']);
  7 + if (createTime != null) {
  8 + readContentEntity.createTime = createTime;
  9 + }
  10 + final String? deleted = jsonConvert.convert<String>(json['deleted']);
  11 + if (deleted != null) {
  12 + readContentEntity.deleted = deleted;
  13 + }
  14 + final String? id = jsonConvert.convert<String>(json['id']);
  15 + if (id != null) {
  16 + readContentEntity.id = id;
  17 + }
  18 + final String? modifyTime = jsonConvert.convert<String>(json['modifyTime']);
  19 + if (modifyTime != null) {
  20 + readContentEntity.modifyTime = modifyTime;
  21 + }
  22 + final int? sortOrder = jsonConvert.convert<int>(json['sortOrder']);
  23 + if (sortOrder != null) {
  24 + readContentEntity.sortOrder = sortOrder;
  25 + }
  26 + final int? status = jsonConvert.convert<int>(json['status']);
  27 + if (status != null) {
  28 + readContentEntity.status = status;
  29 + }
  30 + final int? videoFollowReadId = jsonConvert.convert<int>(json['videoFollowReadId']);
  31 + if (videoFollowReadId != null) {
  32 + readContentEntity.videoFollowReadId = videoFollowReadId;
  33 + }
  34 + final String? videoUrl = jsonConvert.convert<String>(json['videoUrl']);
  35 + if (videoUrl != null) {
  36 + readContentEntity.videoUrl = videoUrl;
  37 + }
  38 + final String? word = jsonConvert.convert<String>(json['word']);
  39 + if (word != null) {
  40 + readContentEntity.word = word;
  41 + }
  42 + return readContentEntity;
  43 +}
  44 +
  45 +Map<String, dynamic> $ReadContentEntityToJson(ReadContentEntity entity) {
  46 + final Map<String, dynamic> data = <String, dynamic>{};
  47 + data['createTime'] = entity.createTime;
  48 + data['deleted'] = entity.deleted;
  49 + data['id'] = entity.id;
  50 + data['modifyTime'] = entity.modifyTime;
  51 + data['sortOrder'] = entity.sortOrder;
  52 + data['status'] = entity.status;
  53 + data['videoFollowReadId'] = entity.videoFollowReadId;
  54 + data['videoUrl'] = entity.videoUrl;
  55 + data['word'] = entity.word;
  56 + return data;
  57 +}
0 58 \ No newline at end of file
... ...
lib/models/read_content_entity.dart 0 → 100644
  1 +import 'package:wow_english/generated/json/base/json_field.dart';
  2 +import 'package:wow_english/generated/json/read_content_entity.g.dart';
  3 +import 'dart:convert';
  4 +
  5 +@JsonSerializable()
  6 +class ReadContentEntity {
  7 + String? createTime;
  8 + String? deleted;
  9 + String? id;
  10 + String? modifyTime;
  11 + int? sortOrder;
  12 + int? status;
  13 + int? videoFollowReadId;
  14 + String? videoUrl;
  15 + String? word;
  16 +
  17 + ReadContentEntity();
  18 +
  19 + factory ReadContentEntity.fromJson(Map<String, dynamic> json) => $ReadContentEntityFromJson(json);
  20 +
  21 + Map<String, dynamic> toJson() => $ReadContentEntityToJson(this);
  22 +
  23 + @override
  24 + String toString() {
  25 + return jsonEncode(this);
  26 + }
  27 +}
0 28 \ No newline at end of file
... ...
lib/pages/login/setpwd/set_pwd_page.dart
... ... @@ -86,43 +86,43 @@ class _SetPassWordPageView extends StatelessWidget {
86 86 }
87 87  
88 88 Widget _buildSetPwdView() => BlocBuilder<SetPwdBloc, SetPwdState>(builder: (context, state) {
89   - return Scaffold(
90   - body: Container(
91   - color: Colors.white,
92   - child: SafeArea(
93   - child: ListView(
94   - children: [
95   - Padding(
96   - padding: EdgeInsets.symmetric(horizontal: 40.w),
97   - child: Column(
  89 + return Scaffold(
  90 + body: Container(
  91 + color: Colors.white,
  92 + child: SafeArea(
  93 + child: ListView(
  94 + children: [
  95 + Padding(
  96 + padding: EdgeInsets.symmetric(horizontal: 40.w),
  97 + child: Column(
  98 + children: [
  99 + 34.verticalSpace,
  100 + Row(
98 101 children: [
99   - 34.verticalSpace,
100   - Row(
101   - children: [
102   - Image.asset(
103   - 'wow_logo'.assetPng,
104   - height: 49.w,
105   - width: 83.5.h,
106   - ),
107   - 12.5.horizontalSpace,
108   - Text(
109   - _getTipsText(),
110   - style: TextStyle(fontSize: 16.5.sp, color: const Color(0xFF666666)),
111   - )
112   - ],
  102 + Image.asset(
  103 + 'wow_logo'.assetPng,
  104 + height: 49.w,
  105 + width: 83.5.h,
113 106 ),
114   - Row(
115   - crossAxisAlignment: CrossAxisAlignment.start,
116   - children: [
117   - Expanded(
118   - child: Column(
119   - mainAxisAlignment: MainAxisAlignment.start,
  107 + 12.5.horizontalSpace,
  108 + Text(
  109 + _getTipsText(),
  110 + style: TextStyle(fontSize: 16.5.sp, color: const Color(0xFF666666)),
  111 + )
  112 + ],
  113 + ),
  114 + Row(
  115 + crossAxisAlignment: CrossAxisAlignment.start,
  116 + children: [
  117 + Expanded(
  118 + child: Column(
  119 + mainAxisAlignment: MainAxisAlignment.start,
  120 + children: [
  121 + 43.verticalSpace,
  122 + Row(
120 123 children: [
121   - 43.verticalSpace,
122   - Row(
123   - children: [
124   - Expanded(
125   - child: TextFieldCustomerWidget(
  124 + Expanded(
  125 + child: TextFieldCustomerWidget(
126 126 height: 55.h,
127 127 hitText: '请输入八位以上密码',
128 128 bgImageName: 'Input_layer_up',
... ... @@ -131,27 +131,27 @@ class _SetPassWordPageView extends StatelessWidget {
131 131 textInputType: TextInputType.emailAddress,
132 132 onChangeValue: (String value) => bloc.add(PwdEnsureEvent()),
133 133 )),
134   - 10.horizontalSpace,
135   - Opacity(
136   - opacity: bloc.showPwdIcon ? 1 : 0,
137   - child: Image.asset(
138   - bloc.passwordEnsure ? 'login_pass'.assetPng : 'login_error'.assetPng,
139   - height: 30,
140   - width: 30,
141   - ),
142   - )
143   - ],
144   - ),
145   - 9.verticalSpace,
146   - Offstage(
147   - offstage: !bloc.passwordLarger,
148   - child: const Text('您已达到密码最大输入数,请妥善调整密码'),
149   - ),
150   - 9.verticalSpace,
151   - Row(
152   - children: [
153   - Expanded(
154   - child: TextFieldCustomerWidget(
  134 + 10.horizontalSpace,
  135 + Opacity(
  136 + opacity: bloc.showPwdIcon ? 1 : 0,
  137 + child: Image.asset(
  138 + bloc.passwordEnsure ? 'login_pass'.assetPng : 'login_error'.assetPng,
  139 + height: 30,
  140 + width: 30,
  141 + ),
  142 + )
  143 + ],
  144 + ),
  145 + 9.verticalSpace,
  146 + Offstage(
  147 + offstage: !bloc.passwordLarger,
  148 + child: const Text('您已达到密码最大输入数,请妥善调整密码'),
  149 + ),
  150 + 9.verticalSpace,
  151 + Row(
  152 + children: [
  153 + Expanded(
  154 + child: TextFieldCustomerWidget(
155 155 height: 55.h,
156 156 hitText: '请再次输入相同密码',
157 157 bgImageName: 'Input_layer_up',
... ... @@ -160,70 +160,70 @@ class _SetPassWordPageView extends StatelessWidget {
160 160 controller: bloc.passWordSecondController,
161 161 onChangeValue: (String value) => bloc.add(PwdCheckEvent()),
162 162 )),
163   - 10.horizontalSpace,
164   - Opacity(
165   - opacity: bloc.showCheckPwdIcon ? 1 : 0,
166   - child: Image.asset(
167   - bloc.passwordCheck ? 'login_pass'.assetPng : 'login_error'.assetPng,
168   - height: 30,
169   - width: 30,
170   - ),
171   - )
172   - ],
173   - ),
174   - 9.verticalSpace,
175   - Offstage(
176   - offstage: bloc.passwordCheck,
177   - child: Text(
178   - '请确认两次输入的密码是否一致',
179   - style: TextStyle(fontSize: 16.sp, color: const Color(0xFF333333)),
  163 + 10.horizontalSpace,
  164 + Opacity(
  165 + opacity: bloc.showCheckPwdIcon ? 1 : 0,
  166 + child: Image.asset(
  167 + bloc.passwordCheck ? 'login_pass'.assetPng : 'login_error'.assetPng,
  168 + height: 30,
  169 + width: 30,
180 170 ),
181   - ),
182   - Row(
183   - mainAxisAlignment: MainAxisAlignment.end,
184   - children: [
185   - GestureDetector(
186   - onTap: () {
187   - if (!bloc.ensure) {
188   - return;
189   - }
190   - bloc.add(SetPasswordEvent());
191   - },
192   - child: Container(
193   - decoration: BoxDecoration(
194   - image: DecorationImage(
195   - image: AssetImage(
196   - bloc.ensure ? 'login_enter'.assetPng : 'login_enter_dis'.assetPng),
197   - fit: BoxFit.fill),
198   - ),
199   - padding: EdgeInsets.symmetric(horizontal: 28.w, vertical: 14.h),
200   - child: Text(
201   - '确定',
202   - style: TextStyle(color: Colors.white, fontSize: 16.sp),
203   - ),
204   - ),
205   - ),
206   - 50.horizontalSpace
207   - ],
208 171 )
209 172 ],
210 173 ),
211   - ),
212   - 30.horizontalSpace,
213   - Image.asset(
214   - 'steven'.assetPng,
215   - height: 254.h,
216   - width: 100.w,
217   - )
218   - ],
  174 + 9.verticalSpace,
  175 + Offstage(
  176 + offstage: bloc.passwordCheck,
  177 + child: Text(
  178 + '请确认两次输入的密码是否一致',
  179 + style: TextStyle(fontSize: 16.sp, color: const Color(0xFF333333)),
  180 + ),
  181 + ),
  182 + Row(
  183 + mainAxisAlignment: MainAxisAlignment.end,
  184 + children: [
  185 + GestureDetector(
  186 + onTap: () {
  187 + if (!bloc.ensure) {
  188 + return;
  189 + }
  190 + bloc.add(SetPasswordEvent());
  191 + },
  192 + child: Container(
  193 + decoration: BoxDecoration(
  194 + image: DecorationImage(
  195 + image: AssetImage(
  196 + bloc.ensure ? 'login_enter'.assetPng : 'login_enter_dis'.assetPng),
  197 + fit: BoxFit.fill),
  198 + ),
  199 + padding: EdgeInsets.symmetric(horizontal: 28.w, vertical: 14.h),
  200 + child: Text(
  201 + '确定',
  202 + style: TextStyle(color: Colors.white, fontSize: 16.sp),
  203 + ),
  204 + ),
  205 + ),
  206 + 50.horizontalSpace
  207 + ],
  208 + )
  209 + ],
  210 + ),
219 211 ),
  212 + 30.horizontalSpace,
  213 + Image.asset(
  214 + 'steven'.assetPng,
  215 + height: 254.h,
  216 + width: 100.w,
  217 + )
220 218 ],
221 219 ),
222   - )
223   - ],
224   - ),
225   - ),
  220 + ],
  221 + ),
  222 + )
  223 + ],
226 224 ),
227   - );
228   - });
  225 + ),
  226 + ),
  227 + );
  228 + });
229 229 }
... ...
lib/pages/practice/bloc/topic_picture_bloc.dart
... ... @@ -198,13 +198,9 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
198 198 ///先声评测结果
199 199 void _voiceXsResult(XSVoiceResultEvent event,Emitter<TopicPictureState> emitter) async {
200 200 final Map args = event.message as Map;
201   - final result = args['result'] as String;
202   - if (result == '1') {
203   - final overall = args['overall'].toString();
204   - showToast('测评成功,分数是$overall',duration: const Duration(seconds: 5));
205   - } else {
206   - showToast('测评失败',duration: const Duration(seconds: 5));
207   - }
  201 + final result = args['result'] as Map;
  202 + final overall = result['overall'].toString();
  203 + showToast('测评成功,分数是$overall',duration: const Duration(seconds: 5));
208 204 _isVoicing = false;
209 205 emitter(XSVoiceTestState());
210 206 }
... ...
lib/pages/repeatafter/repeat_after_page.dart
... ... @@ -48,7 +48,7 @@ class _RepeatAfterPageView extends StatelessWidget {
48 48 FollowReadEntity? entity = bloc.listData[index];
49 49 return RepeatAfterItem(
50 50 tapEvent: () {
51   - pushNamed(AppRouteName.readAfterContent);
  51 + pushNamed(AppRouteName.readAfterContent,arguments: {'videoFollowReadId':entity?.id});
52 52 },
53 53 entity: entity,
54 54 );
... ...
lib/pages/repeataftercontent/bloc/repeat_after_content_bloc.dart 0 → 100644
  1 +import 'package:audioplayers/audioplayers.dart';
  2 +import 'package:flutter/cupertino.dart';
  3 +import 'package:flutter/services.dart';
  4 +import 'package:flutter_bloc/flutter_bloc.dart';
  5 +import 'package:flutter_easyloading/flutter_easyloading.dart';
  6 +import 'package:wow_english/common/request/dao/listen_dao.dart';
  7 +
  8 +import '../../../common/request/exception.dart';
  9 +import '../../../models/read_content_entity.dart';
  10 +import '../../../utils/loading.dart';
  11 +import '../../../utils/toast_util.dart';
  12 +
  13 +
  14 +part 'repeat_after_content_event.dart';
  15 +part 'repeat_after_content_state.dart';
  16 +
  17 +enum VoiceRecordState {
  18 + ///未知
  19 + voiceRecordUnkonw,
  20 + ///开始录音
  21 + voiceRecordStat,
  22 + ///正在录音
  23 + voiceRecording,
  24 + ///录音结束
  25 + voiceRecordEnd
  26 +}
  27 +
  28 +enum VoicePlayState {
  29 + ///未知
  30 + unKnow,
  31 + ///播放中
  32 + playing,
  33 + ///播放完成
  34 + completed,
  35 + ///播放终止
  36 + stop
  37 +}
  38 +
  39 +class RepeatAfterContentBloc extends Bloc<RepeatAfterContentEvent, RepeatAfterContentState> {
  40 +
  41 + final String courseLessonId;
  42 +
  43 + ///是否正在播放视频
  44 + bool _videoPlaying = true;
  45 + ///是否需要录音
  46 + bool _isRecord = false;
  47 +
  48 + Map? _voiceTestResult;
  49 +
  50 + VoiceRecordState _voiceRecordState = VoiceRecordState.voiceRecordUnkonw;
  51 +
  52 + bool get videoPlaying => _videoPlaying;
  53 +
  54 + bool get isRecord => _isRecord;
  55 +
  56 + VoiceRecordState get voiceRecordState => _voiceRecordState;
  57 +
  58 + List<ReadContentEntity?>? _entityList;
  59 +
  60 + List<ReadContentEntity?>? get entityList => _entityList ;
  61 +
  62 + Map? get voiceTestResult => _voiceTestResult;
  63 +
  64 + late MethodChannel methodChannel;
  65 +
  66 + late AudioPlayer audioPlayer;
  67 +
  68 + RepeatAfterContentBloc(this.courseLessonId) : super(RepeatAfterContentInitial()) {
  69 + on<VoiceRecordStateChangeEvent>(_voiceRecordStateChange);
  70 + on<VideoPlayChangeEvent>(_videoPlayStateChange);
  71 + on<RecordeVoicePlayEvent>(_recordeVoicePlay);
  72 + on<XSVoiceResultEvent>(_voiceXsResult);
  73 + on<XSVoiceInitEvent>(_initVoiceSdk);
  74 + on<RequestDataEvent>(_requestData);
  75 + on<XSVoiceTestEvent>(_voiceXsTest);
  76 + on<XSVoiceStopEvent>(_voiceXsStop);
  77 + on<VoiceRecordEvent>(_voiceRecord);
  78 + on<InitBlocEvent>((event, emit) {
  79 + //音频播放器
  80 + audioPlayer = AudioPlayer();
  81 + audioPlayer.onPlayerStateChanged.listen((event) async {
  82 + debugPrint('播放状态变化');
  83 + if (event == PlayerState.completed) {
  84 + debugPrint('播放完成');
  85 +
  86 + }
  87 + if (event == PlayerState.stopped) {
  88 + debugPrint('播放结束');
  89 +
  90 + }
  91 +
  92 + if (event == PlayerState.playing) {
  93 + debugPrint('正在播放中');
  94 +
  95 + }
  96 + if(isClosed) {
  97 + return;
  98 + }
  99 +
  100 + });
  101 +
  102 + methodChannel = const MethodChannel('wow_english/sing_sound_method_channel');
  103 + methodChannel.setMethodCallHandler((call) async {
  104 + if (call.method == 'voiceResult') {//评测结果
  105 + add(XSVoiceResultEvent(call.arguments));
  106 + return;
  107 + }
  108 +
  109 + if (call.method == 'voiceStart') {//评测开始
  110 + debugPrint('评测开始');
  111 + return;
  112 + }
  113 +
  114 + if (call.method == 'voiceEnd') {//评测结束
  115 + debugPrint('评测结束');
  116 + return;
  117 + }
  118 +
  119 + if (call.method == 'voiceFail') {//评测失败
  120 + EasyLoading.showToast('评测失败');
  121 + return;
  122 + }
  123 + });
  124 + });
  125 + }
  126 +
  127 + ///请求数据
  128 + void _requestData(RequestDataEvent event,Emitter<RepeatAfterContentState> emitter) async {
  129 + try {
  130 + await loading(() async {
  131 + _entityList = await ListenDao.readContent(courseLessonId);
  132 + emitter(RequestDataState());
  133 + });
  134 + } catch (e) {
  135 + if (e is ApiException) {
  136 + showToast(e.message??'请求失败,请检查网络连接');
  137 + }
  138 + }
  139 + }
  140 +
  141 + void _videoPlayStateChange(VideoPlayChangeEvent event,Emitter<RepeatAfterContentState> emitter) async {
  142 + _videoPlaying = !_videoPlaying;
  143 + emitter(VideoPlayChangeState());
  144 + }
  145 +
  146 + void _voiceRecord(VoiceRecordEvent event,Emitter<RepeatAfterContentState> emitter) async {
  147 + _isRecord = !_isRecord;
  148 + emitter(VoiceRecordChangeState());
  149 + }
  150 +
  151 + void _voiceRecordStateChange(VoiceRecordStateChangeEvent event,Emitter<RepeatAfterContentState> emitter) async {
  152 + _voiceRecordState = event.voiceRecordState;
  153 + emitter(VoiceRecordStateChange());
  154 + }
  155 +
  156 +
  157 + _initVoiceSdk(XSVoiceInitEvent event,Emitter<RepeatAfterContentState> emitter) async {
  158 + methodChannel.invokeMethod('initVoiceSdk',event.data);
  159 + }
  160 +
  161 + ///先声测试
  162 + void _voiceXsTest(XSVoiceTestEvent event,Emitter<RepeatAfterContentState> emitter) async {
  163 + await audioPlayer.stop();
  164 + methodChannel.invokeMethod(
  165 + 'startVoice',
  166 + {'word':event.testWord,'type':event.type,'userId':event.userId.toString()}
  167 + );
  168 + emitter(XSVoiceTestState());
  169 + }
  170 +
  171 + ///终止评测
  172 + void _voiceXsStop(XSVoiceStopEvent event,Emitter<RepeatAfterContentState> emitter) async {
  173 + methodChannel.invokeMethod('stopVoice');
  174 + }
  175 +
  176 + ///先声评测结果
  177 + void _voiceXsResult(XSVoiceResultEvent event,Emitter<RepeatAfterContentState> emitter) async {
  178 + final Map args = event.message as Map;
  179 + final result = args['result'] as Map;
  180 + final overall = result['overall'].toString()??'';
  181 + final audioUrl = args['audioUrl'].toString()??'';
  182 + _voiceTestResult = {'overall':overall,'audioUrl':audioUrl};
  183 + emitter(XSVoiceTestState());
  184 + }
  185 +
  186 + ///播放声音
  187 + void _recordeVoicePlay(RecordeVoicePlayEvent event,Emitter<RepeatAfterContentState> emitter) async {
  188 + await audioPlayer.stop();
  189 + assert(event.audioUrl.isNotEmpty);
  190 + await audioPlayer.play(UrlSource(event.audioUrl));
  191 + }
  192 +}
... ...
lib/pages/repeataftercontent/bloc/repeat_after_content_event.dart 0 → 100644
  1 +part of 'repeat_after_content_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class RepeatAfterContentEvent {}
  5 +
  6 +class InitBlocEvent extends RepeatAfterContentEvent {}
  7 +
  8 +class VideoPlayChangeEvent extends RepeatAfterContentEvent {}
  9 +
  10 +class VoiceRecordEvent extends RepeatAfterContentEvent {}
  11 +
  12 +class RequestDataEvent extends RepeatAfterContentEvent {}
  13 +
  14 +class VoiceRecordStateChangeEvent extends RepeatAfterContentEvent {
  15 + final VoiceRecordState voiceRecordState;
  16 + VoiceRecordStateChangeEvent(this.voiceRecordState);
  17 +}
  18 +
  19 +///初始化先声SDK
  20 +class XSVoiceInitEvent extends RepeatAfterContentEvent {
  21 + final Map data;
  22 + XSVoiceInitEvent(this.data);
  23 +}
  24 +
  25 +///开始评测
  26 +class XSVoiceTestEvent extends RepeatAfterContentEvent {
  27 + final String testWord;
  28 + final String type;
  29 + final String userId;
  30 + XSVoiceTestEvent(this.testWord,this.type,this.userId);
  31 +}
  32 +
  33 +///终止评测
  34 +class XSVoiceStopEvent extends RepeatAfterContentEvent {}
  35 +
  36 +///评测结果
  37 +class XSVoiceResultEvent extends RepeatAfterContentEvent {
  38 + final dynamic message;
  39 + XSVoiceResultEvent(this.message);
  40 +}
  41 +
  42 +class RecordeVoicePlayEvent extends RepeatAfterContentEvent {
  43 + final String audioUrl;
  44 + RecordeVoicePlayEvent(this.audioUrl);
  45 +}
  46 +
  47 +
... ...
lib/pages/repeataftercontent/bloc/repeat_after_content_state.dart 0 → 100644
  1 +part of 'repeat_after_content_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class RepeatAfterContentState {}
  5 +
  6 +class RepeatAfterContentInitial extends RepeatAfterContentState {}
  7 +
  8 +class VideoPlayChangeState extends RepeatAfterContentState {}
  9 +
  10 +class VoiceRecordChangeState extends RepeatAfterContentState {}
  11 +
  12 +class VoiceRecordStateChange extends RepeatAfterContentState {}
  13 +
  14 +class RequestDataState extends RepeatAfterContentState {}
  15 +
  16 +class XSVoiceTestState extends RepeatAfterContentState {}
... ...
lib/pages/repeataftercontent/repeat_after_content_page.dart
1 1 import 'package:flutter/material.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
2 3 import 'package:flutter_screenutil/flutter_screenutil.dart';
3 4 import 'package:wow_english/common/extension/string_extension.dart';
  5 +import 'package:wow_english/pages/repeataftercontent/bloc/repeat_after_content_bloc.dart';
4 6 import 'package:wow_english/route/route.dart';
5 7  
  8 +import '../../common/core/user_util.dart';
  9 +import 'widgets/repeat_video_widget.dart';
  10 +
6 11 class RepeatAfterContentPage extends StatelessWidget {
7   - const RepeatAfterContentPage({super.key});
  12 + const RepeatAfterContentPage({super.key, this.videoFollowReadId});
  13 +
  14 + final String? videoFollowReadId;
8 15  
9 16 @override
10 17 Widget build(BuildContext context) {
  18 + return BlocProvider(
  19 + create: (context) => RepeatAfterContentBloc(videoFollowReadId ??'')
  20 + ..add(InitBlocEvent())
  21 + ..add(RequestDataEvent())
  22 + ..add(XSVoiceInitEvent(
  23 + {
  24 + 'appKey':'a418',
  25 + 'secretKey':'1a16f31f2611bf32fb7b3fc38f5b2c81',
  26 + 'userId':UserUtil.getUser()!.id.toString()
  27 + }
  28 + )),
  29 + child: _RepeatAfterContentPage(),
  30 + );
  31 + }
  32 +}
  33 +
  34 +class _RepeatAfterContentPage extends StatelessWidget {
  35 + @override
  36 + Widget build(BuildContext context) {
  37 + return BlocListener<RepeatAfterContentBloc,RepeatAfterContentState>(
  38 + listener: (context,state){
  39 + debugPrint('123');
  40 + },
  41 + child: _repeatAfterContentView(),
  42 + );
  43 + }
  44 +
  45 +
  46 + Widget _repeatAfterContentView() => BlocBuilder<RepeatAfterContentBloc,RepeatAfterContentState>(builder: (context,state){
  47 + final bloc = BlocProvider.of<RepeatAfterContentBloc>(context);
  48 + final String videoUrl = bloc.entityList?.first?.videoUrl??'';
11 49 return Container(
12 50 color: Colors.white,
13 51 child: SafeArea(
14 52 child: Stack(
15 53 children: [
  54 + ///返回
16 55 Positioned(
17 56 top: 13.h,
18 57 child: GestureDetector(
... ... @@ -23,10 +62,262 @@ class RepeatAfterContentPage extends StatelessWidget {
23 62 width: 40.w,
24 63 ),
25 64 ),
26   - )
  65 + ),
  66 + ///左侧视频区
  67 + Positioned(
  68 + top: 53.h,
  69 + left: 20.w,
  70 + child: Container(
  71 + width: 285.w,
  72 + height: 299.h,
  73 + padding: EdgeInsets.symmetric(horizontal: 50.w,vertical: 50.h),
  74 + decoration: BoxDecoration(
  75 + image: DecorationImage(
  76 + image: AssetImage('video_background'.assetPng),
  77 + fit: BoxFit.fill
  78 + ),
  79 + ),
  80 + child: videoUrl.isEmpty?Container(): RepeatVideoWidget(videoUrl: bloc.entityList?.first?.videoUrl,),
  81 + ),
  82 + ),
  83 + ///右侧操作区
  84 + Positioned(
  85 + top: 53.h,
  86 + right: 25.w,
  87 + child: Container(
  88 + width: 240.w,
  89 + height: 299.h,
  90 + padding: EdgeInsets.only(
  91 + left: 67.w,
  92 + bottom: 40.h
  93 + ),
  94 + decoration: BoxDecoration(
  95 + image: DecorationImage(
  96 + image: AssetImage('light_ground'.assetPng),
  97 + fit: BoxFit.fill
  98 + ),
  99 + ),
  100 + child: bloc.isRecord?_buildLongPressWidget():_buildPlayVideoWidget(),
  101 + ),
  102 + ),
  103 + Positioned(
  104 + top: 72.h,
  105 + left: 274.w,
  106 + child: Container(
  107 + width: 87.w,
  108 + height: 240.h,
  109 + decoration: BoxDecoration(
  110 + image: DecorationImage(
  111 + image: AssetImage('and_book'.assetPng),
  112 + fit: BoxFit.fill
  113 + ),
  114 + ),
  115 + ),
  116 + ),
  117 + ///跟读
  118 + Positioned(
  119 + top: 29.h,
  120 + left: 65.w,
  121 + child: Container(
  122 + width: 185.w,
  123 + height: 48.h,
  124 + decoration: BoxDecoration(
  125 + image: DecorationImage(
  126 + image: AssetImage('title_ground'.assetPng),
  127 + fit: BoxFit.fill
  128 + ),
  129 + ),
  130 + child: Text(
  131 + 'read title',
  132 + textAlign: TextAlign.center,
  133 + style: TextStyle(
  134 + color: Colors.white,
  135 + fontSize: 21.sp
  136 + ),
  137 + ),
  138 + ),
  139 + ),
27 140 ],
28 141 ),
29 142 ),
30 143 );
  144 + });
  145 +
  146 + ///播放中
  147 + Widget _buildPlayVideoWidget() {
  148 + return BlocBuilder<RepeatAfterContentBloc,RepeatAfterContentState>(
  149 + builder: (context,state){
  150 + final bloc = BlocProvider.of<RepeatAfterContentBloc>(context);
  151 + return Column(
  152 + mainAxisAlignment: MainAxisAlignment.end,
  153 + children: [
  154 + Row(
  155 + children: [
  156 + SizedBox(
  157 + height: 23.h,
  158 + width: 33.w,
  159 + ),
  160 + // IconButton(
  161 + // onPressed: (){},
  162 + // icon: Image.asset(
  163 + // 'previous'.assetPng,
  164 + // height: 23.h,
  165 + // width: 23.w,
  166 + // )
  167 + // ),
  168 + IconButton(
  169 + onPressed:() => bloc.add(VideoPlayChangeEvent()),
  170 + icon: Image.asset(
  171 + 'video_pause'.assetPng,
  172 + height: 50.h,
  173 + width: 50.w,
  174 + )
  175 + ),
  176 + SizedBox(
  177 + height: 23.h,
  178 + width: 23.w,
  179 + ),
  180 + // IconButton(
  181 + // onPressed: (){},
  182 + // icon: Image.asset(
  183 + // 'next'.assetPng,
  184 + // height: 23.h,
  185 + // width: 23.w,
  186 + // )
  187 + // )
  188 + ],
  189 + ),
  190 + Row(
  191 + children: [
  192 + SizedBox(
  193 + height: 23.h,
  194 + width: 23.w,
  195 + ),
  196 + 10.horizontalSpace,
  197 + Column(
  198 + children: [
  199 + 20.verticalSpace,
  200 + IconButton(
  201 + onPressed: ()=>bloc.add(VoiceRecordEvent()),
  202 + icon: Image.asset(
  203 + 'video_record'.assetPng,
  204 + height: 53.h,
  205 + width: 53.w,
  206 + )
  207 + ),
  208 + Text(
  209 + '录音',
  210 + style: TextStyle(
  211 + color: const Color(0xFF333333),
  212 + fontSize: 14.sp
  213 + ),
  214 + )
  215 + ],
  216 + ),
  217 + // Container(
  218 + // height: 22.h,
  219 + // width: 37.w,
  220 + // decoration: BoxDecoration(
  221 + // color: const Color(0xFF56CE5F),
  222 + // borderRadius: BorderRadius.circular(10.r)
  223 + // ),
  224 + // child: Text(
  225 + // '1.0x',
  226 + // textAlign: TextAlign.center,
  227 + // style: TextStyle(
  228 + // color: Colors.white,
  229 + // fontSize: 12.sp
  230 + // ),
  231 + // ),
  232 + // )
  233 + ],
  234 + )
  235 + ],
  236 + );
  237 + });
31 238 }
  239 +
  240 + ///长按录音
  241 + Widget _buildLongPressWidget() => BlocBuilder<RepeatAfterContentBloc,RepeatAfterContentState>(
  242 + builder: (context,state){
  243 + final bloc = BlocProvider.of<RepeatAfterContentBloc>(context);
  244 + final voiceResult = bloc.voiceTestResult;
  245 + return Column(
  246 + mainAxisAlignment: MainAxisAlignment.end,
  247 + children: [
  248 + Offstage(
  249 + offstage: bloc.voiceRecordState != VoiceRecordState.voiceRecordEnd && voiceResult == null,
  250 + child: Column(
  251 + children: [
  252 + Container(
  253 + height: 45.h,
  254 + width: 45.h,
  255 + alignment: Alignment.center,
  256 + decoration: BoxDecoration(
  257 + color: const Color(0xFF40E04B),
  258 + borderRadius: BorderRadius.circular(22.5.r)
  259 + ),
  260 + child: Text(
  261 + voiceResult?['overall'].toString()??'0',
  262 + textAlign: TextAlign.center,
  263 + style: TextStyle(
  264 + color: Colors.white,
  265 + fontSize: 17.sp
  266 + ),
  267 + ),
  268 + ),
  269 + IconButton(
  270 + onPressed: (){
  271 + if(voiceResult != null) {
  272 + bloc.add(RecordeVoicePlayEvent(voiceResult['audioUrl']??''));
  273 + }
  274 + },
  275 + icon: Image.asset(
  276 + 'voice_record_play'.assetPng,
  277 + height: 30.h,
  278 + width: 30.w,
  279 + )
  280 + ),
  281 + Text(
  282 + '录音',
  283 + textAlign: TextAlign.center,
  284 + style: TextStyle(
  285 + color: const Color(0xFF666666),
  286 + fontSize: 11.sp
  287 + ),
  288 + ),
  289 + ],
  290 + ),
  291 + ),
  292 + GestureDetector(
  293 + onTap: () => bloc.add(VoiceRecordEvent()),
  294 + onLongPress: () {
  295 + bloc.add(XSVoiceTestEvent(bloc.entityList?.first?.word??'', '0', UserUtil.getUser()!.id.toString()));
  296 + },
  297 + onLongPressStart: (LongPressStartDetails details) {
  298 + bloc.add(VoiceRecordStateChangeEvent(VoiceRecordState.voiceRecordStat));
  299 + },
  300 + onLongPressEnd: (LongPressEndDetails details) {
  301 + bloc.add(VoiceRecordStateChangeEvent(VoiceRecordState.voiceRecordEnd));
  302 + },
  303 + onLongPressUp: () {
  304 +
  305 + },
  306 + child: Image.asset(
  307 + 'video_record'.assetPng,
  308 + height: 78.h,
  309 + width: 78.w,
  310 + ),
  311 + ),
  312 + Text(
  313 + '按住录音',
  314 + textAlign: TextAlign.center,
  315 + style: TextStyle(
  316 + color: const Color(0xFF333333),
  317 + fontSize: 16.sp
  318 + ),
  319 + ),
  320 + ],
  321 + );
  322 + });
32 323 }
33 324 \ No newline at end of file
... ...
lib/pages/repeataftercontent/widgets/repeat_video_widget.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  4 +import 'package:video_player/video_player.dart';
  5 +
  6 +import '../bloc/repeat_after_content_bloc.dart';
  7 +
  8 +class RepeatVideoWidget extends StatefulWidget {
  9 + const RepeatVideoWidget({super.key, this.videoUrl});
  10 + final String? videoUrl;
  11 +
  12 + @override
  13 + State<StatefulWidget> createState() {
  14 + return _RepeatVideoWidgetState();
  15 + }
  16 +}
  17 +
  18 +class _RepeatVideoWidgetState extends State<RepeatVideoWidget> {
  19 + VideoPlayerController? _controller;
  20 + String _currentTime = '00:00';
  21 + String _totalTime = '00:00';
  22 + double _playDegree = 0.5;
  23 +
  24 + String formatDuration(Duration duration) {
  25 + String minutes = duration.inMinutes.remainder(60).toString().padLeft(2, '0');
  26 + String seconds = duration.inSeconds.remainder(60).toString().padLeft(2, '0');
  27 + return "$minutes:$seconds";
  28 + }
  29 +
  30 + void _addListener() {
  31 + _controller!.addListener(() {
  32 + if(_controller!.value.isInitialized) {
  33 + if (_controller!.value.isPlaying) {
  34 + setState(() {
  35 + double currentSecond = (_controller!.value.position.inMinutes.remainder(60)*60+_controller!.value.position.inSeconds.remainder(60)).toDouble();
  36 + int totalSecond = _controller!.value.duration.inMinutes.remainder(60)*60+_controller!.value.duration.inSeconds.remainder(60);
  37 + _currentTime = formatDuration(_controller!.value.position);
  38 + _playDegree = currentSecond/totalSecond;
  39 + if(_playDegree < 0) {
  40 + _playDegree = 0.0;
  41 + }
  42 +
  43 + if(_playDegree > 1) {
  44 + _playDegree = 1.0;
  45 + }
  46 + });
  47 + }
  48 + }
  49 + });
  50 + }
  51 +
  52 + @override
  53 + void initState() {
  54 + super.initState();
  55 + _controller = VideoPlayerController.network(widget.videoUrl??'')
  56 + ..initialize().then((_){
  57 + setState(() {
  58 + _currentTime = formatDuration(_controller!.value.position);
  59 + _totalTime = formatDuration(_controller!.value.duration);
  60 + _controller!.setLooping(true);
  61 + _controller!.setVolume(100);
  62 + _controller!.play();
  63 + });
  64 + _addListener();
  65 + });
  66 + }
  67 +
  68 + @override
  69 + Widget build(BuildContext context) {
  70 + return Center(
  71 + child: _controller!.value.isInitialized ? BlocListener<RepeatAfterContentBloc,RepeatAfterContentState>(
  72 + listener: (context, state){
  73 + if (state is VideoPlayChangeState) {
  74 + if (context.read<RepeatAfterContentBloc>().videoPlaying) {
  75 + _controller!.play();
  76 + } else {
  77 + _controller!.pause();
  78 + }
  79 + }
  80 + },
  81 + child: SizedBox(
  82 + child: Column(
  83 + children: [
  84 + Expanded(
  85 + child: Stack(
  86 + children: [
  87 + Container(
  88 + width: double.infinity,
  89 + height: double.infinity,
  90 + alignment: Alignment.center,
  91 + decoration: BoxDecoration(
  92 + color: Colors.white,
  93 + border: Border.all(
  94 + width: 1.0,
  95 + color: const Color(0xFF140C10),
  96 + ),
  97 + borderRadius: BorderRadius.circular(5.r)
  98 + ),
  99 + child: AspectRatio(
  100 + aspectRatio: _controller!.value.aspectRatio,
  101 + child: VideoPlayer(_controller!),
  102 + ),
  103 + ),
  104 + Positioned(
  105 + left: 0,
  106 + right: 0,
  107 + bottom: 0,
  108 + child: Slider(
  109 + min:0,
  110 + max: 1.0,
  111 + activeColor: const Color(0xFF78B72D),
  112 + inactiveColor: const Color(0xFF7E756C),
  113 + onChangeStart: (value) {
  114 +
  115 + },
  116 + onChangeEnd: (value) {
  117 +
  118 + },
  119 + onChanged: (value) {
  120 +
  121 + }, value: _playDegree,
  122 + ))
  123 + ],
  124 + ),
  125 + ),
  126 + Row(
  127 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  128 + children: [
  129 + Text(_currentTime),
  130 + Text(_totalTime),
  131 + ],
  132 + )
  133 + ],
  134 + ),
  135 + )
  136 + )
  137 + :
  138 + Container(
  139 + color: Colors.white,
  140 + child: Text(
  141 + '视频加载中....',
  142 + style: TextStyle(
  143 + fontSize: 20.sp,
  144 + color: Colors.black
  145 + ),
  146 + ),
  147 + ),
  148 + );
  149 + }
  150 +
  151 + @override
  152 + void dispose() {
  153 + _controller?.dispose();
  154 + _controller?.removeListener(() {});
  155 + super.dispose();
  156 + }
  157 +}
0 158 \ No newline at end of file
... ...
lib/pages/video/lookvideo/widgets/video_widget.dart
1 1 import 'package:common_utils/common_utils.dart';
2   -import 'package:flutter/foundation.dart';
3 2 import 'package:flutter/material.dart';
  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 6  
... ... @@ -169,14 +169,21 @@ class _VideoWidgetState extends State&lt;VideoWidget&gt; {
169 169 },
170 170 icon: Image.asset(
171 171 'video_stop'.assetPng,
172   - width: 70,
173   - height: 70,
  172 + width: 70.w,
  173 + height: 70.h,
174 174 ),
175 175 ),
176 176 )
177 177 ],
178 178 ): Container(
179 179 color: Colors.white,
  180 + child: Text(
  181 + '视频加载中....',
  182 + style: TextStyle(
  183 + fontSize: 20.sp,
  184 + color: Colors.black
  185 + ),
  186 + ),
180 187 ),
181 188 ),
182 189 );
... ...
lib/route/route.dart
... ... @@ -79,8 +79,8 @@ class AppRouter {
79 79 }
80 80 return CupertinoPageRoute(
81 81 builder: (_) => HomePage(
82   - moduleId: moduleId,
83   - ));
  82 + moduleId: moduleId,
  83 + ));
84 84 case AppRouteName.fogPwd:
85 85 return CupertinoPageRoute(builder: (_) => const ForgetPasswordHomePage());
86 86 case AppRouteName.lesson:
... ... @@ -99,7 +99,7 @@ class AppRouter {
99 99 return CupertinoPageRoute(builder: (_) => const UserPage());
100 100 case AppRouteName.userInformation:
101 101 return CupertinoPageRoute(builder: (_) => const UserInformationPage());
102   - /*case AppRouteName.userModifyInformation:
  102 + /*case AppRouteName.userModifyInformation:
103 103 return CupertinoPageRoute(builder: (_) {
104 104 ModifyUserInformationType argument = ModifyUserInformationType.name;
105 105 if (settings.arguments != null) {
... ... @@ -118,16 +118,11 @@ class AppRouter {
118 118 final title = (settings.arguments as Map)['title'] as String?;
119 119 return CupertinoPageRoute(
120 120 builder: (_) => LookVideoPage(
121   - videoUrl: videoUrl,
122   - typeTitle: title,
123   - ));
124   - /*case AppRouteName.setPwd:
125   - var map = settings.arguments as Map;
126   - final phoneNum = map['phoneNumber'] as String?;
127   - final smsCode = map['smsCode'] as String?;
128   - final pageType = map['pageType'] as SetPwdPageType;
129   - return CupertinoPageRoute(
130   - builder: (_) => SetPassWordPage(
  121 + videoUrl: videoUrl,
  122 + typeTitle: title,
  123 + ));
  124 + /*case AppRouteName.setPwd:
  125 + case AppRouteName.setPwd:
131 126 phoneNum: phoneNum,
132 127 smsCode: smsCode,
133 128 pageType: pageType,
... ... @@ -137,12 +132,16 @@ class AppRouter {
137 132 final webViewTitle = (settings.arguments as Map)['webViewTitle'] as String;
138 133 return CupertinoPageRoute(
139 134 builder: (_) => WowWebViewPage(
140   - urlStr: urlStr,
141   - webViewTitle: webViewTitle,
142   - ));
  135 + urlStr: urlStr,
  136 + webViewTitle: webViewTitle,
  137 + ));
143 138 case AppRouteName.readAfterContent:
  139 + var videoFollowReadId = '';
  140 + if (settings.arguments != null) {
  141 + videoFollowReadId = (settings.arguments as Map)['videoFollowReadId'] as String??'';
  142 + }
144 143 return CupertinoPageRoute(
145   - builder: (_) => const RepeatAfterContentPage());
  144 + builder: (_) => RepeatAfterContentPage(videoFollowReadId:videoFollowReadId));
146 145 case AppRouteName.tab:
147 146 return PageRouteBuilder(
148 147 opaque: false,
... ...