Commit 9ebefc69b3f2cb7f85d42d6a85dd19fe2001d885

Authored by xiaoyu
2 parents 22495953 911794fd

Merge remote-tracking branch 'origin/feat-wqf-payment' into xiaoyu_cocossteve

# Conflicts:
#	pubspec.yaml
Showing 53 changed files with 1270 additions and 469 deletions
lib/app/splash_page.dart
... ... @@ -77,7 +77,7 @@ class _TransitionViewState extends State<TransitionView> {
77 77 }*/
78 78 bool isAggreementAccepted = AppConfigHelper.getAgreementAccepted();
79 79 if (isAggreementAccepted) {
80   - pushNamedAndRemoveUntil(AppRouteName.moduleSelect, (route) => false);
  80 + pushNamedAndRemoveUntil(AppRouteName.home, (route) => false);
81 81 } else {
82 82 showDialog(
83 83 context: context,
... ... @@ -91,11 +91,11 @@ class _TransitionViewState extends State<TransitionView> {
91 91 leftTap: () {
92 92 AppConfigHelper.saveAgreementAccepted(true);
93 93 pushNamedAndRemoveUntil(
94   - AppRouteName.moduleSelect, (route) => false);
  94 + AppRouteName.home, (route) => false);
95 95 },
96 96 rightTap: () {
97 97 // 退出应用
98   - SystemNavigator.pop();
  98 + AppConfigHelper.exitApp();
99 99 },
100 100 ),
101 101 );
... ...
lib/common/core/app_config_helper.dart
... ... @@ -2,6 +2,8 @@ import 'dart:ffi';
2 2 import 'dart:io';
3 3  
4 4 import 'package:flutter/cupertino.dart';
  5 +import 'package:flutter/foundation.dart';
  6 +import 'package:flutter/services.dart';
5 7 import 'package:package_info_plus/package_info_plus.dart';
6 8 import 'package:wow_english/common/core/sp_const.dart';
7 9 import 'package:wow_english/common/core/user_util.dart';
... ... @@ -16,6 +18,9 @@ class AppConfigHelper {
16 18  
17 19 static String _versionCode = '';
18 20  
  21 + // 是否检测过更新,因为路由混乱导致首页会多次启动,避免重复检测更新,先临时处理
  22 + static bool checkedUpdate = false;
  23 +
19 24 // 获取用户信息
20 25 static Future<AppConfigEntity?> getAppConfig() async {
21 26 if (configEntityEntity != null) {
... ... @@ -25,9 +30,15 @@ class AppConfigHelper {
25 30 return configEntityEntity;
26 31 }
27 32  
  33 + // 是否是iOS平台
  34 + static bool isIosPlatform() {
  35 + return defaultTargetPlatform == TargetPlatform.iOS;
  36 + }
  37 +
28 38 // 是否需要隐藏...
29 39 static bool shouldHidePay() {
30   - return configEntityEntity?.isAppReviewing() == true || UserUtil.getUser()?.phoneNum == "17730280759";
  40 + return isIosPlatform() &&
  41 + (configEntityEntity?.isAppReviewing() == true || UserUtil.getUser()?.phoneNum == "17730280759");
31 42 }
32 43  
33 44 // 获取app版本号
... ... @@ -55,4 +66,13 @@ class AppConfigHelper {
55 66 static void _clearUserData() {
56 67 SpUtil.getInstance().remove(SpConst.prefsKeyAgreementAccepted);
57 68 }
  69 +
  70 + static void exitApp() {
  71 + if (Platform.isIOS) {
  72 + exit(0);
  73 + } else {
  74 + //此种方式在IOS上不生效
  75 + SystemNavigator.pop();
  76 + }
  77 + }
58 78 }
... ...
lib/common/core/user_util.dart
... ... @@ -60,12 +60,11 @@ class UserUtil {
60 60 if (currentPageName != AppRouteName.splash) {
61 61 Navigator.of(AppRouter.context).pushNamedAndRemoveUntil(AppRouteName.login, (route) => false);
62 62 }*/
63   - Navigator.of(AppRouter.context).pushNamedAndRemoveUntil(AppRouteName.login, (route) => false, arguments: {'showPasswordPage': showPasswordLoginPage});
  63 + pushNamedAndRemoveUntil(AppRouteName.login, (route) => false, arguments: {'showPasswordPage': showPasswordLoginPage});
64 64 }
65 65  
66 66 // 是否有游戏权限
67 67 static bool hasGamePermission() {
68   - debugPrint('hasGamePermission: ${_userEntity}');
69 68 return _userEntity?.valid ?? false;
70 69 }
71 70  
... ...
lib/common/permission/permissionRequestPage.dart
1 1 import 'dart:io';
2 2 import 'package:flutter/material.dart';
3   -import 'package:flutter/services.dart';
4 3 import 'package:permission_handler/permission_handler.dart';
  4 +import 'package:wow_english/common/core/app_config_helper.dart';
5 5  
6 6 import '../../utils/log_util.dart';
7 7  
... ... @@ -167,7 +167,7 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
167 167  
168 168 /// 退出应用程序
169 169 void _quitApp() {
170   - SystemChannels.platform.invokeMethod("SystemNavigator.pop");
  170 + AppConfigHelper.exitApp();
171 171 }
172 172  
173 173 /// 关闭整个权限申请页面
... ...
lib/common/request/apis.dart
... ... @@ -6,6 +6,9 @@ class Apis {
6 6 /// 接口地址:https://app.apifox.com/link/project/2684751/apis/api-89897678
7 7 static const String appConfig = 'system/app/config';
8 8  
  9 + /// app版本信息
  10 + static const String appVersion = 'system/app/version';
  11 +
9 12 /// 登录
10 13 static const String login = 'login';
11 14  
... ... @@ -42,11 +45,17 @@ class Apis {
42 45 // 接口地址:https://app.apifox.com/link/project/2684751/apis/api-89897663
43 46 static const String courseModule = 'home/courseModule';
44 47  
  48 + /// 课程单元列表
  49 + static const String courseUnit = 'home/courseUnit';
  50 +
45 51 /// 课程列表
46 52 // GET /home/courseLesson
47 53 // 接口地址:https://app.apifox.com/link/project/2684751/apis/api-89897662
48 54 static const String courseLesson = 'home/courseLesson';
49 55  
  56 + // 课程环节列表
  57 + static const String courseSection = 'course/courseLesson';
  58 +
50 59 /// 磨耳朵
51 60 /// GET
52 61 static const String ears = 'course/grinding/ears';
... ...
lib/common/request/dao/home_dao.dart renamed to lib/common/request/dao/lesson_dao.dart
1 1 import 'package:wow_english/common/request/request_client.dart';
2 2 import 'package:wow_english/models/course_entity.dart';
  3 +import 'package:wow_english/models/course_section_entity.dart';
3 4  
4 5 import '../../../models/course_module_entity.dart';
  6 +import '../../../models/course_unit_entity.dart';
5 7  
6   -class HomeDao {
7   - ///获取课程模块信息
  8 +class LessonDao {
  9 + ///获取课程模块(列表)信息
8 10 static Future<List<CourseModuleEntity?>?> courseModule() async {
9 11 var data = await requestClient.get<List<CourseModuleEntity>>(Apis.courseModule);
10 12 return data;
11 13 }
12 14  
13   - ///课程列表
14   - static Future<CourseEntity?> courseLesson({String moduleId = ''}) async {
  15 + ///课程单元列表
  16 + static Future<CourseUnitEntity?> courseUnit(int? moduleId) async {
15 17 Map<String, dynamic> mapData = {};
16   - if (moduleId.isNotEmpty) {
  18 + if (moduleId != null) {
17 19 mapData['moduleId'] = moduleId;
18 20 }
  21 + var data = await requestClient.get<CourseUnitEntity>(Apis.courseUnit, queryParameters: mapData);
  22 + return data;
  23 + }
  24 +
  25 + ///课程列表
  26 + static Future<CourseEntity?> courseLesson({int? courseUnitId}) async {
  27 + Map<String, dynamic> mapData = {};
  28 + if (courseUnitId != null) {
  29 + mapData['courseUnitId'] = courseUnitId;
  30 + }
19 31 var data = await requestClient.get<CourseEntity>(Apis.courseLesson, queryParameters: mapData);
20 32 return data;
21 33 }
  34 +
  35 + ///课程(单元)列表
  36 + static Future<List<CourseSectionEntity>?> courseSection({required int courseUnitId}) async {
  37 + Map<String, dynamic> mapData = {};
  38 + mapData['courseUnitId'] = courseUnitId;
  39 + var data = await requestClient.get<List<CourseSectionEntity>?>(Apis.courseSection, queryParameters: mapData);
  40 + return data;
  41 + }
22 42 }
... ...
lib/common/request/dao/system_dao.dart
1 1 import '../../../models/app_config_entity.dart';
  2 +import '../../../models/app_version_entity.dart';
2 3 import '../request_client.dart';
3 4  
4 5 class SystemDao {
... ... @@ -7,4 +8,9 @@ class SystemDao {
7 8 static Future<AppConfigEntity?> getAppConfig() async {
8 9 return await requestClient.get(Apis.appConfig);
9 10 }
  11 +
  12 + // 获取app版本信息
  13 + static Future<AppVersionEntity?> getVersionInfo() async {
  14 + return await requestClient.get(Apis.appVersion);
  15 + }
10 16 }
... ...
lib/common/request/token_interceptor.dart
1 1 import 'package:dio/dio.dart';
  2 +import 'package:flutter/foundation.dart';
2 3 import 'package:wow_english/common/core/user_util.dart';
3 4  
4 5 import '../core/app_config_helper.dart';
... ... @@ -13,6 +14,7 @@ class TokenInterceptor extends Interceptor {
13 14 // 在发送请求之前获取版本号
14 15 String version = await AppConfigHelper.getAppVersion();
15 16 options.headers["version"] = version;
  17 + options.headers["User-Agent"] = AppConfigHelper.isIosPlatform() ? "ios" : "android";
16 18 super.onRequest(options, handler);
17 19 }
18 20 }
... ...
lib/common/widgets/webview_dialog.dart
... ... @@ -41,21 +41,26 @@ class WebviewDialog extends StatelessWidget {
41 41 }
42 42 })),
43 43 actions: <Widget>[
44   - TextButton(
45   - child:
  44 + Row(
  45 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  46 + children: [
  47 + TextButton(
  48 + child:
46 49 const Text('同意并继续', style: TextStyle(color: Color(0xFFFBB621))),
47   - onPressed: () {
48   - // 处理接受按钮的点击事件
49   - leftTap(); // 关闭对话框
50   - },
51   - ),
52   - TextButton(
53   - child: const Text('不同意,退出应用'),
54   - onPressed: () {
55   - // 处理拒绝按钮的点击事件
56   - rightTap(); // 关闭对话框
57   - },
58   - ),
  50 + onPressed: () {
  51 + // 处理接受按钮的点击事件
  52 + leftTap(); // 关闭对话框
  53 + },
  54 + ),
  55 + TextButton(
  56 + child: const Text('不同意,退出应用'),
  57 + onPressed: () {
  58 + // 处理拒绝按钮的点击事件
  59 + rightTap(); // 关闭对话框
  60 + },
  61 + ),
  62 + ],
  63 + )
59 64 ],
60 65 );
61 66 }
... ...
lib/generated/json/app_config_entity.g.dart
1 1 import 'package:wow_english/generated/json/base/json_convert_content.dart';
2 2 import 'package:wow_english/models/app_config_entity.dart';
3 3  
4   -AppConfigEntity $AppConfigEntityEntityFromJson(
5   - Map<String, dynamic> json) {
6   - final AppConfigEntity appConfigEntityEntity = AppConfigEntity();
7   - final bool? androidForceUpdate = jsonConvert.convert<bool>(
8   - json['androidForceUpdate']);
9   - if (androidForceUpdate != null) {
10   - appConfigEntityEntity.androidForceUpdate = androidForceUpdate;
11   - }
12   - final bool? androidRecommendUpdate = jsonConvert.convert<bool>(
13   - json['androidRecommendUpdate']);
14   - if (androidRecommendUpdate != null) {
15   - appConfigEntityEntity.androidRecommendUpdate = androidRecommendUpdate;
16   - }
17   - final String? androidUpdatePackageUrl = jsonConvert.convert<String>(
18   - json['androidUpdatePackageUrl']);
19   - if (androidUpdatePackageUrl != null) {
20   - appConfigEntityEntity.androidUpdatePackageUrl = androidUpdatePackageUrl;
21   - }
22   - final int? androidVersion = jsonConvert.convert<int>(json['androidVersion']);
23   - if (androidVersion != null) {
24   - appConfigEntityEntity.androidVersion = androidVersion;
25   - }
26   - final bool? iosForceUpdate = jsonConvert.convert<bool>(
27   - json['iosForceUpdate']);
28   - if (iosForceUpdate != null) {
29   - appConfigEntityEntity.iosForceUpdate = iosForceUpdate;
30   - }
31   - final bool? iosRecommendUpdate = jsonConvert.convert<bool>(
32   - json['iosRecommendUpdate']);
33   - if (iosRecommendUpdate != null) {
34   - appConfigEntityEntity.iosRecommendUpdate = iosRecommendUpdate;
35   - }
36   - final int? iosVersion = jsonConvert.convert<int>(json['iosVersion']);
37   - if (iosVersion != null) {
38   - appConfigEntityEntity.iosVersion = iosVersion;
39   - }
40   - final String? noticeBeforePurchaseUrl = jsonConvert.convert<String>(
41   - json['noticeBeforePurchaseUrl']);
42   - if (noticeBeforePurchaseUrl != null) {
43   - appConfigEntityEntity.noticeBeforePurchaseUrl = noticeBeforePurchaseUrl;
44   - }
  4 +AppConfigEntity $AppConfigEntityFromJson(Map<String, dynamic> json) {
  5 + final AppConfigEntity appConfigEntity = AppConfigEntity();
45 6 final String? safe = jsonConvert.convert<String>(json['safe']);
46 7 if (safe != null) {
47   - appConfigEntityEntity.safe = safe;
  8 + appConfigEntity.safe = safe;
48 9 }
49   - return appConfigEntityEntity;
  10 + return appConfigEntity;
50 11 }
51 12  
52   -Map<String, dynamic> $AppConfigEntityEntityToJson(
53   - AppConfigEntity entity) {
  13 +Map<String, dynamic> $AppConfigEntityToJson(AppConfigEntity entity) {
54 14 final Map<String, dynamic> data = <String, dynamic>{};
55   - data['androidForceUpdate'] = entity.androidForceUpdate;
56   - data['androidRecommendUpdate'] = entity.androidRecommendUpdate;
57   - data['androidUpdatePackageUrl'] = entity.androidUpdatePackageUrl;
58   - data['androidVersion'] = entity.androidVersion;
59   - data['iosForceUpdate'] = entity.iosForceUpdate;
60   - data['iosRecommendUpdate'] = entity.iosRecommendUpdate;
61   - data['iosVersion'] = entity.iosVersion;
62   - data['noticeBeforePurchaseUrl'] = entity.noticeBeforePurchaseUrl;
63 15 data['safe'] = entity.safe;
64 16 return data;
65 17 }
66 18  
67   -extension AppConfigEntityEntityExtension on AppConfigEntity {
  19 +extension AppConfigEntityExtension on AppConfigEntity {
68 20 AppConfigEntity copyWith({
69   - bool? androidForceUpdate,
70   - bool? androidRecommendUpdate,
71   - String? androidUpdatePackageUrl,
72   - int? androidVersion,
73   - bool? iosForceUpdate,
74   - bool? iosRecommendUpdate,
75   - int? iosVersion,
76   - String? noticeBeforePurchaseUrl,
77 21 String? safe,
78 22 }) {
79 23 return AppConfigEntity()
80   - ..androidForceUpdate = androidForceUpdate ?? this.androidForceUpdate
81   - ..androidRecommendUpdate = androidRecommendUpdate ??
82   - this.androidRecommendUpdate
83   - ..androidUpdatePackageUrl = androidUpdatePackageUrl ??
84   - this.androidUpdatePackageUrl
85   - ..androidVersion = androidVersion ?? this.androidVersion
86   - ..iosForceUpdate = iosForceUpdate ?? this.iosForceUpdate
87   - ..iosRecommendUpdate = iosRecommendUpdate ?? this.iosRecommendUpdate
88   - ..iosVersion = iosVersion ?? this.iosVersion
89   - ..noticeBeforePurchaseUrl = noticeBeforePurchaseUrl ??
90   - this.noticeBeforePurchaseUrl
91 24 ..safe = safe ?? this.safe;
92 25 }
93 26 }
94 27 \ No newline at end of file
... ...
lib/generated/json/app_version_entity.g.dart 0 → 100644
  1 +import 'package:wow_english/generated/json/base/json_convert_content.dart';
  2 +import 'package:wow_english/models/app_version_entity.dart';
  3 +
  4 +AppVersionEntity $AppVersionEntityFromJson(Map<String, dynamic> json) {
  5 + final AppVersionEntity appVersionEntity = AppVersionEntity();
  6 + final String? packageUrl = jsonConvert.convert<String>(json['packageUrl']);
  7 + if (packageUrl != null) {
  8 + appVersionEntity.packageUrl = packageUrl;
  9 + }
  10 + final String? packageName = jsonConvert.convert<String>(json['packageName']);
  11 + if (packageName != null) {
  12 + appVersionEntity.packageName = packageName;
  13 + }
  14 + final String? packageSize = jsonConvert.convert<String>(json['packageSize']);
  15 + if (packageSize != null) {
  16 + appVersionEntity.packageSize = packageSize;
  17 + }
  18 + final String? platformType = jsonConvert.convert<String>(
  19 + json['platformType']);
  20 + if (platformType != null) {
  21 + appVersionEntity.platformType = platformType;
  22 + }
  23 + final String? remark = jsonConvert.convert<String>(json['remark']);
  24 + if (remark != null) {
  25 + appVersionEntity.remark = remark;
  26 + }
  27 + final String? status = jsonConvert.convert<String>(json['status']);
  28 + if (status != null) {
  29 + appVersionEntity.status = status;
  30 + }
  31 + final String? version = jsonConvert.convert<String>(json['version']);
  32 + if (version != null) {
  33 + appVersionEntity.version = version;
  34 + }
  35 + final String? volType = jsonConvert.convert<String>(json['volType']);
  36 + if (volType != null) {
  37 + appVersionEntity.volType = volType;
  38 + }
  39 + return appVersionEntity;
  40 +}
  41 +
  42 +Map<String, dynamic> $AppVersionEntityToJson(AppVersionEntity entity) {
  43 + final Map<String, dynamic> data = <String, dynamic>{};
  44 + data['packageUrl'] = entity.packageUrl;
  45 + data['packageName'] = entity.packageName;
  46 + data['packageSize'] = entity.packageSize;
  47 + data['platformType'] = entity.platformType;
  48 + data['remark'] = entity.remark;
  49 + data['status'] = entity.status;
  50 + data['version'] = entity.version;
  51 + data['volType'] = entity.volType;
  52 + return data;
  53 +}
  54 +
  55 +extension AppVersionEntityExtension on AppVersionEntity {
  56 + AppVersionEntity copyWith({
  57 + String? packageUrl,
  58 + String? packageName,
  59 + String? packageSize,
  60 + String? platformType,
  61 + String? remark,
  62 + String? status,
  63 + String? version,
  64 + String? volType,
  65 + }) {
  66 + return AppVersionEntity()
  67 + ..packageUrl = packageUrl ?? this.packageUrl
  68 + ..packageName = packageName ?? this.packageName
  69 + ..packageSize = packageSize ?? this.packageSize
  70 + ..platformType = platformType ?? this.platformType
  71 + ..remark = remark ?? this.remark
  72 + ..status = status ?? this.status
  73 + ..version = version ?? this.version
  74 + ..volType = volType ?? this.volType;
  75 + }
  76 +}
0 77 \ No newline at end of file
... ...
lib/generated/json/base/json_convert_content.dart
... ... @@ -6,9 +6,12 @@
6 6 import 'package:flutter/material.dart' show debugPrint;
7 7 import 'package:wow_english/models/aliyun_oss_upload_sts_entity.dart';
8 8 import 'package:wow_english/models/app_config_entity.dart';
  9 +import 'package:wow_english/models/app_version_entity.dart';
9 10 import 'package:wow_english/models/course_entity.dart';
10 11 import 'package:wow_english/models/course_module_entity.dart';
11 12 import 'package:wow_english/models/course_process_entity.dart';
  13 +import 'package:wow_english/models/course_section_entity.dart';
  14 +import 'package:wow_english/models/course_unit_entity.dart';
12 15 import 'package:wow_english/models/follow_read_entity.dart';
13 16 import 'package:wow_english/models/listen_entity.dart';
14 17 import 'package:wow_english/models/product_entity.dart';
... ... @@ -159,6 +162,10 @@ class JsonConvert {
159 162 return data.map<AppConfigEntity>((Map<String, dynamic> e) =>
160 163 AppConfigEntity.fromJson(e)).toList() as M;
161 164 }
  165 + if (<AppVersionEntity>[] is M) {
  166 + return data.map<AppVersionEntity>((Map<String, dynamic> e) =>
  167 + AppVersionEntity.fromJson(e)).toList() as M;
  168 + }
162 169 if (<CourseEntity>[] is M) {
163 170 return data.map<CourseEntity>((Map<String, dynamic> e) =>
164 171 CourseEntity.fromJson(e)).toList() as M;
... ... @@ -192,6 +199,18 @@ class JsonConvert {
192 199 return data.map<CourseProcessVideos>((Map<String, dynamic> e) =>
193 200 CourseProcessVideos.fromJson(e)).toList() as M;
194 201 }
  202 + if (<CourseSectionEntity>[] is M) {
  203 + return data.map<CourseSectionEntity>((Map<String, dynamic> e) =>
  204 + CourseSectionEntity.fromJson(e)).toList() as M;
  205 + }
  206 + if (<CourseUnitEntity>[] is M) {
  207 + return data.map<CourseUnitEntity>((Map<String, dynamic> e) =>
  208 + CourseUnitEntity.fromJson(e)).toList() as M;
  209 + }
  210 + if (<CourseUnitDetail>[] is M) {
  211 + return data.map<CourseUnitDetail>((Map<String, dynamic> e) =>
  212 + CourseUnitDetail.fromJson(e)).toList() as M;
  213 + }
195 214 if (<FollowReadEntity>[] is M) {
196 215 return data.map<FollowReadEntity>((Map<String, dynamic> e) =>
197 216 FollowReadEntity.fromJson(e)).toList() as M;
... ... @@ -237,6 +256,7 @@ class JsonConvertClassCollection {
237 256 (AliyunOssUploadStsCallbackParam)
238 257 .toString(): AliyunOssUploadStsCallbackParam.fromJson,
239 258 (AppConfigEntity).toString(): AppConfigEntity.fromJson,
  259 + (AppVersionEntity).toString(): AppVersionEntity.fromJson,
240 260 (CourseEntity).toString(): CourseEntity.fromJson,
241 261 (CourseCourseLessons).toString(): CourseCourseLessons.fromJson,
242 262 (CourseModuleEntity).toString(): CourseModuleEntity.fromJson,
... ... @@ -246,6 +266,9 @@ class JsonConvertClassCollection {
246 266 (CourseProcessTopicsTopicAnswerList)
247 267 .toString(): CourseProcessTopicsTopicAnswerList.fromJson,
248 268 (CourseProcessVideos).toString(): CourseProcessVideos.fromJson,
  269 + (CourseSectionEntity).toString(): CourseSectionEntity.fromJson,
  270 + (CourseUnitEntity).toString(): CourseUnitEntity.fromJson,
  271 + (CourseUnitDetail).toString(): CourseUnitDetail.fromJson,
249 272 (FollowReadEntity).toString(): FollowReadEntity.fromJson,
250 273 (ListenEntity).toString(): ListenEntity.fromJson,
251 274 (ProductEntity).toString(): ProductEntity.fromJson,
... ...
lib/generated/json/course_module_entity.g.dart
... ... @@ -3,7 +3,7 @@ import &#39;package:wow_english/models/course_module_entity.dart&#39;;
3 3  
4 4 CourseModuleEntity $CourseModuleEntityFromJson(Map<String, dynamic> json) {
5 5 final CourseModuleEntity courseModuleEntity = CourseModuleEntity();
6   - final String? id = jsonConvert.convert<String>(json['id']);
  6 + final int? id = jsonConvert.convert<int>(json['id']);
7 7 if (id != null) {
8 8 courseModuleEntity.id = id;
9 9 }
... ... @@ -90,7 +90,7 @@ Map&lt;String, dynamic&gt; $CourseModuleEntityToJson(CourseModuleEntity entity) {
90 90  
91 91 extension CourseModuleEntityExtension on CourseModuleEntity {
92 92 CourseModuleEntity copyWith({
93   - String? id,
  93 + int? id,
94 94 String? code,
95 95 int? courseModuleThemeId,
96 96 int? courseTotal,
... ...
lib/generated/json/course_section_entity.g.dart 0 → 100644
  1 +import 'package:wow_english/generated/json/base/json_convert_content.dart';
  2 +import 'package:wow_english/models/course_section_entity.dart';
  3 +
  4 +CourseSectionEntity $CourseSectionEntityFromJson(Map<String, dynamic> json) {
  5 + final CourseSectionEntity courseSectionEntity = CourseSectionEntity();
  6 + final int? id = jsonConvert.convert<int>(json['id']);
  7 + if (id != null) {
  8 + courseSectionEntity.id = id;
  9 + }
  10 + final int? courseUnitId = jsonConvert.convert<int>(json['courseUnitId']);
  11 + if (courseUnitId != null) {
  12 + courseSectionEntity.courseUnitId = courseUnitId;
  13 + }
  14 + final int? courseModuleId = jsonConvert.convert<int>(json['courseModuleId']);
  15 + if (courseModuleId != null) {
  16 + courseSectionEntity.courseModuleId = courseModuleId;
  17 + }
  18 + final String? name = jsonConvert.convert<String>(json['name']);
  19 + if (name != null) {
  20 + courseSectionEntity.name = name;
  21 + }
  22 + final dynamic des = json['des'];
  23 + if (des != null) {
  24 + courseSectionEntity.des = des;
  25 + }
  26 + final int? courseType = jsonConvert.convert<int>(json['courseType']);
  27 + if (courseType != null) {
  28 + courseSectionEntity.courseType = courseType;
  29 + }
  30 + final dynamic coverUrl = json['coverUrl'];
  31 + if (coverUrl != null) {
  32 + courseSectionEntity.coverUrl = coverUrl;
  33 + }
  34 + final int? sortOrder = jsonConvert.convert<int>(json['sortOrder']);
  35 + if (sortOrder != null) {
  36 + courseSectionEntity.sortOrder = sortOrder;
  37 + }
  38 + final int? status = jsonConvert.convert<int>(json['status']);
  39 + if (status != null) {
  40 + courseSectionEntity.status = status;
  41 + }
  42 + final bool? lock = jsonConvert.convert<bool>(json['lock']);
  43 + if (lock != null) {
  44 + courseSectionEntity.lock = lock;
  45 + }
  46 + return courseSectionEntity;
  47 +}
  48 +
  49 +Map<String, dynamic> $CourseSectionEntityToJson(CourseSectionEntity entity) {
  50 + final Map<String, dynamic> data = <String, dynamic>{};
  51 + data['id'] = entity.id;
  52 + data['courseUnitId'] = entity.courseUnitId;
  53 + data['courseModuleId'] = entity.courseModuleId;
  54 + data['name'] = entity.name;
  55 + data['des'] = entity.des;
  56 + data['courseType'] = entity.courseType;
  57 + data['coverUrl'] = entity.coverUrl;
  58 + data['sortOrder'] = entity.sortOrder;
  59 + data['status'] = entity.status;
  60 + data['lock'] = entity.lock;
  61 + return data;
  62 +}
  63 +
  64 +extension CourseSectionEntityExtension on CourseSectionEntity {
  65 + CourseSectionEntity copyWith({
  66 + int? id,
  67 + int? courseUnitId,
  68 + int? courseModuleId,
  69 + String? name,
  70 + dynamic des,
  71 + int? courseType,
  72 + dynamic coverUrl,
  73 + int? sortOrder,
  74 + int? status,
  75 + bool? lock,
  76 + }) {
  77 + return CourseSectionEntity()
  78 + ..id = id ?? this.id
  79 + ..courseUnitId = courseUnitId ?? this.courseUnitId
  80 + ..courseModuleId = courseModuleId ?? this.courseModuleId
  81 + ..name = name ?? this.name
  82 + ..des = des ?? this.des
  83 + ..courseType = courseType ?? this.courseType
  84 + ..coverUrl = coverUrl ?? this.coverUrl
  85 + ..sortOrder = sortOrder ?? this.sortOrder
  86 + ..status = status ?? this.status
  87 + ..lock = lock ?? this.lock;
  88 + }
  89 +}
0 90 \ No newline at end of file
... ...
lib/generated/json/course_unit_entity.g.dart 0 → 100644
  1 +import 'package:wow_english/generated/json/base/json_convert_content.dart';
  2 +import 'package:wow_english/models/course_unit_entity.dart';
  3 +
  4 +CourseUnitEntity $CourseUnitEntityFromJson(Map<String, dynamic> json) {
  5 + final CourseUnitEntity courseUnitEntity = CourseUnitEntity();
  6 + final List<
  7 + CourseUnitDetail>? courseUnitVOList = (json['courseUnitVOList'] as List<
  8 + dynamic>?)
  9 + ?.map(
  10 + (e) => jsonConvert.convert<CourseUnitDetail>(e) as CourseUnitDetail)
  11 + .toList();
  12 + if (courseUnitVOList != null) {
  13 + courseUnitEntity.courseUnitVOList = courseUnitVOList;
  14 + }
  15 + final int? nowStep = jsonConvert.convert<int>(json['nowStep']);
  16 + if (nowStep != null) {
  17 + courseUnitEntity.nowStep = nowStep;
  18 + }
  19 + final int? total = jsonConvert.convert<int>(json['total']);
  20 + if (total != null) {
  21 + courseUnitEntity.total = total;
  22 + }
  23 + final int? nowCourseModuleId = jsonConvert.convert<int>(
  24 + json['nowCourseModuleId']);
  25 + if (nowCourseModuleId != null) {
  26 + courseUnitEntity.nowCourseModuleId = nowCourseModuleId;
  27 + }
  28 + final String? nowCourseModuleName = jsonConvert.convert<String>(
  29 + json['nowCourseModuleName']);
  30 + if (nowCourseModuleName != null) {
  31 + courseUnitEntity.nowCourseModuleName = nowCourseModuleName;
  32 + }
  33 + final String? courseModuleThemeColor = jsonConvert.convert<String>(
  34 + json['courseModuleThemeColor']);
  35 + if (courseModuleThemeColor != null) {
  36 + courseUnitEntity.courseModuleThemeColor = courseModuleThemeColor;
  37 + }
  38 + final String? courseModuleCode = jsonConvert.convert<String>(
  39 + json['courseModuleCode']);
  40 + if (courseModuleCode != null) {
  41 + courseUnitEntity.courseModuleCode = courseModuleCode;
  42 + }
  43 + return courseUnitEntity;
  44 +}
  45 +
  46 +Map<String, dynamic> $CourseUnitEntityToJson(CourseUnitEntity entity) {
  47 + final Map<String, dynamic> data = <String, dynamic>{};
  48 + data['courseUnitVOList'] =
  49 + entity.courseUnitVOList?.map((v) => v.toJson()).toList();
  50 + data['nowStep'] = entity.nowStep;
  51 + data['total'] = entity.total;
  52 + data['nowCourseModuleId'] = entity.nowCourseModuleId;
  53 + data['nowCourseModuleName'] = entity.nowCourseModuleName;
  54 + data['courseModuleThemeColor'] = entity.courseModuleThemeColor;
  55 + data['courseModuleCode'] = entity.courseModuleCode;
  56 + return data;
  57 +}
  58 +
  59 +extension CourseUnitEntityExtension on CourseUnitEntity {
  60 + CourseUnitEntity copyWith({
  61 + List<CourseUnitDetail>? courseUnitVOList,
  62 + int? nowStep,
  63 + int? total,
  64 + int? nowCourseModuleId,
  65 + String? nowCourseModuleName,
  66 + String? courseModuleThemeColor,
  67 + String? courseModuleCode,
  68 + }) {
  69 + return CourseUnitEntity()
  70 + ..courseUnitVOList = courseUnitVOList ?? this.courseUnitVOList
  71 + ..nowStep = nowStep ?? this.nowStep
  72 + ..total = total ?? this.total
  73 + ..nowCourseModuleId = nowCourseModuleId ?? this.nowCourseModuleId
  74 + ..nowCourseModuleName = nowCourseModuleName ?? this.nowCourseModuleName
  75 + ..courseModuleThemeColor = courseModuleThemeColor ??
  76 + this.courseModuleThemeColor
  77 + ..courseModuleCode = courseModuleCode ?? this.courseModuleCode;
  78 + }
  79 +}
  80 +
  81 +CourseUnitDetail $CourseUnitDetailFromJson(Map<String, dynamic> json) {
  82 + final CourseUnitDetail courseUnitDetail = CourseUnitDetail();
  83 + final int? courseModuleId = jsonConvert.convert<int>(json['courseModuleId']);
  84 + if (courseModuleId != null) {
  85 + courseUnitDetail.courseModuleId = courseModuleId;
  86 + }
  87 + final int? id = jsonConvert.convert<int>(json['id']);
  88 + if (id != null) {
  89 + courseUnitDetail.id = id;
  90 + }
  91 + final String? name = jsonConvert.convert<String>(json['name']);
  92 + if (name != null) {
  93 + courseUnitDetail.name = name;
  94 + }
  95 + final String? coverUrl = jsonConvert.convert<String>(json['coverUrl']);
  96 + if (coverUrl != null) {
  97 + courseUnitDetail.coverUrl = coverUrl;
  98 + }
  99 + final bool? lock = jsonConvert.convert<bool>(json['lock']);
  100 + if (lock != null) {
  101 + courseUnitDetail.lock = lock;
  102 + }
  103 + final int? sortOrder = jsonConvert.convert<int>(json['sortOrder']);
  104 + if (sortOrder != null) {
  105 + courseUnitDetail.sortOrder = sortOrder;
  106 + }
  107 + final int? status = jsonConvert.convert<int>(json['status']);
  108 + if (status != null) {
  109 + courseUnitDetail.status = status;
  110 + }
  111 + return courseUnitDetail;
  112 +}
  113 +
  114 +Map<String, dynamic> $CourseUnitDetailToJson(CourseUnitDetail entity) {
  115 + final Map<String, dynamic> data = <String, dynamic>{};
  116 + data['courseModuleId'] = entity.courseModuleId;
  117 + data['id'] = entity.id;
  118 + data['name'] = entity.name;
  119 + data['coverUrl'] = entity.coverUrl;
  120 + data['lock'] = entity.lock;
  121 + data['sortOrder'] = entity.sortOrder;
  122 + data['status'] = entity.status;
  123 + return data;
  124 +}
  125 +
  126 +extension CourseUnitDetailExtension on CourseUnitDetail {
  127 + CourseUnitDetail copyWith({
  128 + int? courseModuleId,
  129 + int? id,
  130 + String? name,
  131 + String? coverUrl,
  132 + bool? lock,
  133 + int? sortOrder,
  134 + int? status,
  135 + }) {
  136 + return CourseUnitDetail()
  137 + ..courseModuleId = courseModuleId ?? this.courseModuleId
  138 + ..id = id ?? this.id
  139 + ..name = name ?? this.name
  140 + ..coverUrl = coverUrl ?? this.coverUrl
  141 + ..lock = lock ?? this.lock
  142 + ..sortOrder = sortOrder ?? this.sortOrder
  143 + ..status = status ?? this.status;
  144 + }
  145 +}
0 146 \ No newline at end of file
... ...
lib/models/app_config_entity.dart
... ... @@ -7,40 +7,15 @@ import &#39;../generated/json/app_config_entity.g.dart&#39;;
7 7 @JsonSerializable()
8 8 class AppConfigEntity {
9 9  
10   - // 安卓是否强制更新
11   - bool? androidForceUpdate;
12   -
13   - // 安卓是否推荐更新
14   - bool? androidRecommendUpdate;
15   -
16   - // 安卓更新包地址
17   - String? androidUpdatePackageUrl;
18   -
19   - // 安卓当前版本号
20   - late int androidVersion;
21   -
22   - bool? iosForceUpdate;
23   -
24   - bool? iosRecommendUpdate;
25   -
26   - // ios版本
27   - late int iosVersion;
28   -
29   - // 更新说明
30   - String? updatePackageDescription;
31   -
32   - // 购前须知图片
33   - String? noticeBeforePurchaseUrl;
34   -
35 10 // 当前是否安全,safe-安全 otherwise-隐藏pay
36 11 String? safe;
37 12  
38 13  
39 14 AppConfigEntity();
40 15  
41   - factory AppConfigEntity.fromJson(Map<String, dynamic> json) => $AppConfigEntityEntityFromJson(json);
  16 + factory AppConfigEntity.fromJson(Map<String, dynamic> json) => $AppConfigEntityFromJson(json);
42 17  
43   - Map<String, dynamic> toJson() => $AppConfigEntityEntityToJson(this);
  18 + Map<String, dynamic> toJson() => $AppConfigEntityToJson(this);
44 19  
45 20 @override
46 21 String toString() {
... ...
lib/models/app_version_entity.dart 0 → 100644
  1 +import 'package:wow_english/generated/json/base/json_field.dart';
  2 +import 'package:wow_english/generated/json/app_version_entity.g.dart';
  3 +import 'dart:convert';
  4 +export 'package:wow_english/generated/json/app_version_entity.g.dart';
  5 +
  6 +@JsonSerializable()
  7 +class AppVersionEntity {
  8 +
  9 + // 更新包地址
  10 + String? packageUrl;
  11 +
  12 + // 更新包名
  13 + String? packageName;
  14 +
  15 + // 更新包大小
  16 + String? packageSize;
  17 +
  18 + // 平台类型
  19 + String? platformType;
  20 +
  21 + // 更新说明
  22 + String? remark;
  23 +
  24 + // 状态
  25 + String? status;
  26 +
  27 + // app版本号
  28 + String? version;
  29 +
  30 + // 强更类型
  31 + String? volType;
  32 +
  33 +
  34 + AppVersionEntity();
  35 +
  36 + factory AppVersionEntity.fromJson(Map<String, dynamic> json) => $AppVersionEntityFromJson(json);
  37 +
  38 + Map<String, dynamic> toJson() => $AppVersionEntityToJson(this);
  39 +
  40 + @override
  41 + String toString() {
  42 + return jsonEncode(this);
  43 + }
  44 +}
  45 +
  46 +enum UpdateStrategy {
  47 + SUGGEST("suggest", "建议更新"),
  48 + FORCE("force", "强制更新");
  49 +
  50 + const UpdateStrategy(this.name, this.chineseName);
  51 +
  52 + final String name;
  53 +
  54 + final String chineseName;
  55 +}
0 56 \ No newline at end of file
... ...
lib/models/course_module_entity.dart
... ... @@ -5,7 +5,7 @@ import &#39;package:wow_english/generated/json/course_module_entity.g.dart&#39;;
5 5  
6 6 @JsonSerializable()
7 7 class CourseModuleEntity {
8   - late String id;
  8 + int? id;
9 9 String? code;
10 10 int? courseModuleThemeId;
11 11 int? courseTotal;
... ...
lib/models/course_section_entity.dart 0 → 100644
  1 +import 'package:wow_english/generated/json/base/json_field.dart';
  2 +import 'package:wow_english/generated/json/course_section_entity.g.dart';
  3 +import 'dart:convert';
  4 +export 'package:wow_english/generated/json/course_section_entity.g.dart';
  5 +
  6 +@JsonSerializable()
  7 +class CourseSectionEntity {
  8 + late int id;
  9 + late int courseUnitId;
  10 + late int courseModuleId;
  11 + late String name;
  12 + dynamic des;
  13 + late int courseType;
  14 + dynamic coverUrl;
  15 + late int sortOrder;
  16 + late int status;
  17 + late bool lock;
  18 +
  19 + CourseSectionEntity();
  20 +
  21 + factory CourseSectionEntity.fromJson(Map<String, dynamic> json) => $CourseSectionEntityFromJson(json);
  22 +
  23 + Map<String, dynamic> toJson() => $CourseSectionEntityToJson(this);
  24 +
  25 + @override
  26 + String toString() {
  27 + return jsonEncode(this);
  28 + }
  29 +}
0 30 \ No newline at end of file
... ...
lib/models/course_unit_entity.dart 0 → 100644
  1 +import 'package:wow_english/generated/json/base/json_field.dart';
  2 +import 'package:wow_english/generated/json/course_unit_entity.g.dart';
  3 +import 'dart:convert';
  4 +
  5 +export 'package:wow_english/generated/json/course_unit_entity.g.dart';
  6 +
  7 +@JsonSerializable()
  8 +class CourseUnitEntity {
  9 +
  10 + // 课程详情列表
  11 + List<CourseUnitDetail>? courseUnitVOList;
  12 +
  13 + // 当前进行了多少节课程
  14 + int? nowStep;
  15 +
  16 + // 当前模块一共多少节课程
  17 + int? total;
  18 +
  19 + // 当前模块id
  20 + int? nowCourseModuleId;
  21 +
  22 + // 当前模块名
  23 + String? nowCourseModuleName;
  24 +
  25 + // 主题颜色值
  26 + String? courseModuleThemeColor;
  27 +
  28 + // 课程模块code
  29 + String? courseModuleCode;
  30 +
  31 + CourseUnitEntity();
  32 +
  33 + factory CourseUnitEntity.fromJson(Map<String, dynamic> json) => $CourseUnitEntityFromJson(json);
  34 +
  35 + Map<String, dynamic> toJson() => $CourseUnitEntityToJson(this);
  36 +
  37 + @override
  38 + String toString() {
  39 + return jsonEncode(this);
  40 + }
  41 +}
  42 +
  43 +
  44 +@JsonSerializable()
  45 +class CourseUnitDetail {
  46 +
  47 + // 模块id
  48 + int? courseModuleId;
  49 +
  50 + // 单元
  51 + int? id;
  52 +
  53 + // 单元名称
  54 + String? name;
  55 +
  56 + // 单元封面
  57 + String? coverUrl;
  58 +
  59 + bool? lock;
  60 +
  61 + int? sortOrder;
  62 +
  63 + int? status;
  64 +
  65 + CourseUnitDetail();
  66 +
  67 + factory CourseUnitDetail.fromJson(Map<String, dynamic> json) => $CourseUnitDetailFromJson(json);
  68 +
  69 + Map<String, dynamic> toJson() => $CourseUnitDetailToJson(this);
  70 +
  71 + @override
  72 + String toString() {
  73 + return jsonEncode(this);
  74 + }
  75 +}
0 76 \ No newline at end of file
... ...
lib/pages/moduleSelect/bloc.dart renamed to lib/pages/home/bloc.dart
1 1 import 'package:bloc/bloc.dart';
2 2 import 'package:flutter/cupertino.dart';
3 3 import 'package:flutter/foundation.dart';
4   -import 'package:wow_english/models/app_config_entity.dart';
5 4  
6 5 import '../../common/core/app_config_helper.dart';
  6 +import '../../common/request/dao/system_dao.dart';
  7 +import '../../models/app_version_entity.dart';
  8 +import '../../utils/log_util.dart';
7 9 import 'event.dart';
8 10 import 'state.dart';
9 11  
10   -class ModuleSelectBloc extends Bloc<ModuleSelectEvent, ModuleSelectState> {
11   - ModuleSelectBloc() : super(ModuleSelectState().init()) {
  12 +class ModuleSelectBloc extends Bloc<HomeEvent, HomeState> {
  13 + ModuleSelectBloc() : super(HomeState().init()) {
12 14 on<InitEvent>(_init);
13 15 }
14 16  
15   - void _init(InitEvent event, Emitter<ModuleSelectState> emit) async {
  17 + void _init(InitEvent event, Emitter<HomeState> emit) async {
16 18 await _checkUpdate(emit);
17 19 debugPrint('WQF ModuleSelectBloc _init');
18 20 }
19 21  
20   - Future<void> _checkUpdate(Emitter<ModuleSelectState> emit) async {
  22 + Future<void> _checkUpdate(Emitter<HomeState> emit) async {
  23 + if (AppConfigHelper.checkedUpdate) {
  24 + return;
  25 + }
21 26 int localVersion = int.parse(await AppConfigHelper.getAppVersion());
22   - AppConfigEntity? appConfigEntity = await AppConfigHelper.getAppConfig();
23   - if (appConfigEntity == null) {
  27 + AppVersionEntity? appVersionEntity = await SystemDao.getVersionInfo();
  28 + AppConfigHelper.checkedUpdate = true;
  29 + if (appVersionEntity == null) {
24 30 return;
25 31 }
26   - debugPrint('WQF _checkUpdate');
27   - if (defaultTargetPlatform == TargetPlatform.iOS) {
28   - if (localVersion < appConfigEntity.iosVersion &&
29   - appConfigEntity.iosRecommendUpdate == true) {
30   - emit(UpdateDialogState(
31   - appConfigEntity.iosForceUpdate ?? false, appConfigEntity));
32   - }
33   - } else {
34   - if (localVersion < appConfigEntity.androidVersion &&
35   - appConfigEntity.androidRecommendUpdate == true) {
36   - emit(UpdateDialogState(
37   - appConfigEntity.androidForceUpdate ?? false, appConfigEntity,
38   - ));
39   - }
  32 + Log.d("WQF _checkUpdate appVersionEntity: $appVersionEntity localVersion=$localVersion");
  33 + if (localVersion < int.parse(appVersionEntity.version ?? '0')) {
  34 + emit(UpdateDialogState(
  35 + appVersionEntity.volType == UpdateStrategy.FORCE.name, appVersionEntity));
40 36 }
41 37 }
42 38 }
... ...
lib/pages/home/event.dart 0 → 100644
  1 +abstract class HomeEvent {}
  2 +
  3 +class InitEvent extends HomeEvent {}
0 4 \ No newline at end of file
... ...
lib/pages/home/state.dart 0 → 100644
  1 +import 'package:wow_english/models/app_version_entity.dart';
  2 +
  3 +class HomeState {
  4 + HomeState init() {
  5 + return HomeState();
  6 + }
  7 +
  8 + HomeState clone() {
  9 + return HomeState();
  10 + }
  11 +}
  12 +
  13 +class UpdateDialogState extends HomeState {
  14 +
  15 + final AppVersionEntity appVersionEntity;
  16 +
  17 + final bool forceUpdate;
  18 +
  19 + UpdateDialogState(this.forceUpdate, this.appVersionEntity);
  20 +}
... ...
lib/pages/moduleSelect/view.dart renamed to lib/pages/home/view.dart
... ... @@ -8,20 +8,21 @@ import &#39;package:flutter_bloc/flutter_bloc.dart&#39;;
8 8 import 'package:url_launcher/url_launcher.dart';
9 9 import 'package:wow_english/common/core/app_config_helper.dart';
10 10 import 'package:wow_english/common/extension/string_extension.dart';
11   -import 'package:wow_english/pages/moduleSelect/state.dart';
12   -import 'package:wow_english/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart';
  11 +import 'package:wow_english/models/app_version_entity.dart';
  12 +import 'package:wow_english/pages/home/state.dart';
  13 +import 'package:wow_english/pages/home/widgets/BaseHomeHeaderWidget.dart';
13 14 import 'package:wow_english/pages/user/bloc/user_bloc.dart';
14 15  
15 16 import '../../common/core/user_util.dart';
16 17 import '../../common/dialogs/show_dialog.dart';
17   -import '../../models/app_config_entity.dart';
  18 +import '../../utils/log_util.dart';
18 19 import 'bloc.dart';
19 20 import 'event.dart';
20 21 import 'package:flutter_screenutil/flutter_screenutil.dart';
21 22 import 'package:wow_english/route/route.dart';
22 23  
23   -class ModuleSelectPage extends StatelessWidget {
24   - const ModuleSelectPage({super.key});
  24 +class HomePage extends StatelessWidget {
  25 + const HomePage({super.key});
25 26  
26 27 @override
27 28 Widget build(BuildContext context) {
... ... @@ -41,10 +42,11 @@ class _HomePageView extends StatelessWidget {
41 42 BlocListener<UserBloc, UserState>(listener: (context, state) {
42 43 debugPrint('WQF ModuleSelectPage BlocListener state: $state');
43 44 }),
44   - BlocListener<ModuleSelectBloc, ModuleSelectState>(
  45 + BlocListener<ModuleSelectBloc, HomeState>(
45 46 listener: (context, state) {
  47 + Log.d("WQF HomePage listener state: $state");
46 48 if (state is UpdateDialogState) {
47   - _showUpdateDialog(context, state.forceUpdate, state.appConfigEntity);
  49 + _showUpdateDialog(context, state.forceUpdate, state.appVersionEntity);
48 50 }
49 51 },
50 52 ),
... ... @@ -52,7 +54,7 @@ class _HomePageView extends StatelessWidget {
52 54 }
53 55  
54 56 Widget _homeView() =>
55   - BlocBuilder<ModuleSelectBloc, ModuleSelectState>(
  57 + BlocBuilder<ModuleSelectBloc, HomeState>(
56 58 builder: (context, state) {
57 59 return Scaffold(
58 60 body: Container(
... ... @@ -68,7 +70,7 @@ class _HomePageView extends StatelessWidget {
68 70 child: GestureDetector(
69 71 onTap: () {
70 72 if (UserUtil.isLogined()) {
71   - pushNamed(AppRouteName.home);
  73 + pushNamed(AppRouteName.courseUnit);
72 74 } else {
73 75 pushNamed(AppRouteName.login);
74 76 }
... ... @@ -169,7 +171,7 @@ class _HomePageView extends StatelessWidget {
169 171 ///Flutter侧处理升级对话框
170 172 ///[forcedUpgrade] 是否强制升级
171 173 _showUpdateDialog(BuildContext context, bool forcedUpgrade,
172   - AppConfigEntity appConfigEntity) {
  174 + AppVersionEntity appVersionEntity) {
173 175 showDialog(
174 176 context: context,
175 177 // 当我们点击除开对话框内容以外的区域是否关闭对话需用用到barrierDismissible参数 . 这个参数默认值是true ,但不能为null .
... ... @@ -180,7 +182,7 @@ class _HomePageView extends StatelessWidget {
180 182 child: AlertDialog(
181 183 title: const Text('发现新版本'),
182 184 content: Text(
183   - appConfigEntity.updatePackageDescription ??
  185 + appVersionEntity.remark ??
184 186 '修复了一些已知问题'),
185 187 actions: <Widget>[
186 188 TextButton(
... ... @@ -188,7 +190,7 @@ class _HomePageView extends StatelessWidget {
188 190 onPressed: () =>
189 191 {
190 192 if (forcedUpgrade) {
191   - SystemNavigator.pop()
  193 + AppConfigHelper.exitApp()
192 194 } else
193 195 {
194 196 Navigator.of(context).pop()
... ... @@ -198,11 +200,11 @@ class _HomePageView extends StatelessWidget {
198 200 TextButton(
199 201 child: const Text('升级'),
200 202 onPressed: () async {
201   - if (defaultTargetPlatform == TargetPlatform.iOS) {
  203 + if (AppConfigHelper.isIosPlatform()) {
202 204 _launchAppStore("6450870731");
203 205 return;
204 206 }
205   - final String? apkUrl = appConfigEntity.androidUpdatePackageUrl;
  207 + final String? apkUrl = appVersionEntity.packageUrl;
206 208 if (apkUrl == null || apkUrl.isEmpty) {
207 209 return;
208 210 }
... ...
lib/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart renamed to lib/pages/home/widgets/BaseHomeHeaderWidget.dart
... ... @@ -78,8 +78,8 @@ class BaseHomeHeaderWidget extends StatelessWidget {
78 78 textAlign: TextAlign.left,
79 79 style: TextStyle(color: Colors.white, fontSize: 30.0),
80 80 )),
81   - Visibility(
82   - visible: !AppConfigHelper.shouldHidePay(),
  81 + Offstage(
  82 + offstage: AppConfigHelper.shouldHidePay(),
83 83 child: Row(children: <Widget>[
84 84 Image(
85 85 width: 20.0.w,
... ...
lib/pages/lessons/bloc/lesson_state.dart deleted
1   -part of 'lesson_bloc.dart';
2   -
3   -@immutable
4   -abstract class LessonState {}
5   -
6   -class LessonInitial extends LessonState {}
7   -
8   -class PageIndexChangeState extends LessonState {}
9   -
10   -class LessonDataLoadState extends LessonState {}
lib/pages/login/loginpage/login_page.dart
... ... @@ -34,7 +34,7 @@ class _LoginPageView extends StatelessWidget {
34 34 if (state is LoginResultChangeState) {
35 35 // 调试用
36 36 // Navigator.of(context).pushNamed(AppRouteName.home);
37   - pushNamedAndRemoveUntil(AppRouteName.moduleSelect, (route) => false);
  37 + pushNamedAndRemoveUntil(AppRouteName.home, (route) => false);
38 38 }
39 39 },
40 40 child: _buildLoginViewWidget(),
... ...
lib/pages/lessons/bloc/lesson_bloc.dart renamed to lib/pages/module/bloc/module_bloc.dart
1 1 import 'package:flutter/cupertino.dart';
2 2 import 'package:flutter_bloc/flutter_bloc.dart';
3   -import 'package:wow_english/common/request/dao/home_dao.dart';
  3 +import 'package:wow_english/common/request/dao/lesson_dao.dart';
4 4 import 'package:wow_english/common/request/exception.dart';
5 5 import 'package:wow_english/models/course_module_entity.dart';
6 6 import 'package:wow_english/utils/loading.dart';
7 7 import 'package:wow_english/utils/toast_util.dart';
8 8  
9   -part 'lesson_event.dart';
10   -part 'lesson_state.dart';
  9 +part 'module_event.dart';
  10 +part 'module_state.dart';
11 11  
12   -class LessonBloc extends Bloc<LessonEvent, LessonState> {
  12 +class ModuleBloc extends Bloc<ModuleEvent, ModuleState> {
13 13 final int pageIndex;
14 14  
15 15 final PageController pageController;
... ... @@ -22,22 +22,22 @@ class LessonBloc extends Bloc&lt;LessonEvent, LessonState&gt; {
22 22  
23 23 List<CourseModuleEntity?> get listData => _listData;
24 24  
25   - LessonBloc(this.pageIndex, this.pageController) : super(LessonInitial()) {
  25 + ModuleBloc(this.pageIndex, this.pageController) : super(ModuleInitial()) {
26 26 _currentPageIndex = pageIndex;
27 27 on<PageViewChangeIndexEvent>(_pageIndexChange);
28 28 on<RequestDataEvent>(_requestData);
29 29 }
30 30  
31   - void _pageIndexChange(PageViewChangeIndexEvent event, Emitter<LessonState> emitter) async {
  31 + void _pageIndexChange(PageViewChangeIndexEvent event, Emitter<ModuleState> emitter) async {
32 32 _currentPageIndex = event.index;
33 33 emitter(PageIndexChangeState());
34 34 }
35 35  
36   - void _requestData(RequestDataEvent event, Emitter<LessonState> emitter) async {
  36 + void _requestData(RequestDataEvent event, Emitter<ModuleState> emitter) async {
37 37 try {
38 38 await loading(() async {
39   - _listData = await HomeDao.courseModule() ?? [];
40   - emitter(LessonDataLoadState());
  39 + _listData = await LessonDao.courseModule() ?? [];
  40 + emitter(ModuleDataLoadState());
41 41 });
42 42 } catch (e) {
43 43 if (e is ApiException) {
... ...
lib/pages/lessons/bloc/lesson_event.dart renamed to lib/pages/module/bloc/module_event.dart
1   -part of 'lesson_bloc.dart';
  1 +part of 'module_bloc.dart';
2 2  
3 3 @immutable
4   -abstract class LessonEvent {}
  4 +abstract class ModuleEvent {}
5 5  
6   -class PageViewChangeIndexEvent extends LessonEvent {
  6 +class PageViewChangeIndexEvent extends ModuleEvent {
7 7 final int index;
8 8 PageViewChangeIndexEvent(this.index);
9 9 }
10 10  
11   -class RequestDataEvent extends LessonEvent {}
  11 +class RequestDataEvent extends ModuleEvent {}
... ...
lib/pages/module/bloc/module_state.dart 0 → 100644
  1 +part of 'module_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class ModuleState {}
  5 +
  6 +class ModuleInitial extends ModuleState {}
  7 +
  8 +class PageIndexChangeState extends ModuleState {}
  9 +
  10 +class ModuleDataLoadState extends ModuleState {}
... ...
lib/pages/lessons/lesson_page.dart renamed to lib/pages/module/module_page.dart
... ... @@ -6,23 +6,21 @@ import &#39;package:wow_english/common/widgets/we_app_bar.dart&#39;;
6 6 import 'package:wow_english/models/course_module_entity.dart';
7 7 import 'package:wow_english/route/route.dart';
8 8  
9   -import 'bloc/lesson_bloc.dart';
10   -import 'widgets/lesson_item_widget.dart';
  9 +import 'bloc/module_bloc.dart';
  10 +import 'widgets/module_item_widget.dart';
11 11  
12   -class LessonPage extends StatelessWidget {
13   - const LessonPage({super.key, this.starPageIndex});
  12 +// 阶段(模块)列表页
  13 +class ModulePage extends StatelessWidget {
  14 + const ModulePage({super.key, this.starPageIndex});
14 15  
15 16 final int? starPageIndex;
16 17  
17 18 @override
18 19 Widget build(BuildContext context) {
19 20 return BlocProvider(
20   - create: (context) => LessonBloc(
21   - starPageIndex??0,
22   - PageController(
23   - initialPage: starPageIndex??0,
24   - viewportFraction: 0.3
25   - ),
  21 + create: (context) => ModuleBloc(
  22 + starPageIndex ?? 0,
  23 + PageController(initialPage: starPageIndex ?? 0, viewportFraction: 0.3),
26 24 )..add(RequestDataEvent()),
27 25 child: _LessonPageView(),
28 26 );
... ... @@ -30,27 +28,25 @@ class LessonPage extends StatelessWidget {
30 28 }
31 29  
32 30 class _LessonPageView extends StatelessWidget {
33   -
34 31 final double _cardHeight = 240.h;
35 32  
36 33 final double _scale = 0.8;
37 34  
38 35 @override
39 36 Widget build(BuildContext context) {
40   - return BlocListener<LessonBloc,LessonState>(
41   - listener: (context, state){},
  37 + return BlocListener<ModuleBloc, ModuleState>(
  38 + listener: (context, state) {},
42 39 child: Scaffold(
43 40 appBar: WEAppBar(
44 41 leading: IconButton(
45   - onPressed: (){
46   - popPage();
  42 + onPressed: () {
  43 + popPage();
47 44 },
48 45 icon: Image.asset(
49 46 'back'.assetPng,
50 47 height: 43,
51 48 width: 43,
52   - )
53   - ),
  49 + )),
54 50 // actions: <Widget>[
55 51 // IconButton(
56 52 // icon: Image.asset('shop'.assetPng),
... ... @@ -66,9 +62,9 @@ class _LessonPageView extends StatelessWidget {
66 62 );
67 63 }
68 64  
69   - Widget _lessViewWidget() => BlocBuilder<LessonBloc,LessonState>(
70   - builder: (context, state){
71   - final bloc = BlocProvider.of<LessonBloc>(context);
  65 + Widget _lessViewWidget() =>
  66 + BlocBuilder<ModuleBloc, ModuleState>(builder: (context, state) {
  67 + final bloc = BlocProvider.of<ModuleBloc>(context);
72 68 return Center(
73 69 child: SafeArea(
74 70 child: Column(
... ... @@ -81,8 +77,7 @@ class _LessonPageView extends StatelessWidget {
81 77 onPageChanged: (int index) {
82 78 bloc.add(PageViewChangeIndexEvent(index));
83 79 },
84   - itemBuilder: (context,index) => _itemTransCard(index)
85   - ),
  80 + itemBuilder: (context, index) => _itemTransCard(index)),
86 81 ),
87 82 32.verticalSpace,
88 83 SizedBox(
... ... @@ -91,7 +86,7 @@ class _LessonPageView extends StatelessWidget {
91 86 child: ListView.builder(
92 87 itemCount: bloc.listData.length,
93 88 scrollDirection: Axis.horizontal,
94   - itemBuilder: (BuildContext context,int index){
  89 + itemBuilder: (BuildContext context, int index) {
95 90 return Container(
96 91 height: 32.h,
97 92 width: 66.w,
... ... @@ -101,13 +96,19 @@ class _LessonPageView extends StatelessWidget {
101 96 if (index == bloc.currentPageIndex) {
102 97 return;
103 98 }
104   - int mill = (index - bloc.currentPageIndex) > 0 ? 100 * (index - bloc.currentPageIndex):100 * (bloc.currentPageIndex-index);
105   - bloc.pageController.animateToPage(index, duration: Duration(milliseconds: mill), curve: Curves.ease);
  99 + int mill = (index - bloc.currentPageIndex) > 0
  100 + ? 100 * (index - bloc.currentPageIndex)
  101 + : 100 * (bloc.currentPageIndex - index);
  102 + bloc.pageController.animateToPage(index,
  103 + duration: Duration(milliseconds: mill),
  104 + curve: Curves.ease);
106 105 },
107 106 child: Container(
108   - height: bloc.currentPageIndex == index ? 32:20,
  107 + height: bloc.currentPageIndex == index ? 32 : 20,
109 108 decoration: BoxDecoration(
110   - color: bloc.currentPageIndex == index ? Colors.red:Colors.white,
  109 + color: bloc.currentPageIndex == index
  110 + ? Colors.red
  111 + : Colors.white,
111 112 borderRadius: BorderRadius.circular(5.r),
112 113 border: Border.all(
113 114 width: 0.5,
... ... @@ -116,10 +117,11 @@ class _LessonPageView extends StatelessWidget {
116 117 ),
117 118 alignment: Alignment.center,
118 119 child: Text(
119   - (index+1).toString(),
  120 + (index + 1).toString(),
120 121 style: TextStyle(
121   - color: bloc.currentPageIndex == index ? Colors.white:Colors.black
122   - ),
  122 + color: bloc.currentPageIndex == index
  123 + ? Colors.white
  124 + : Colors.black),
123 125 ),
124 126 ),
125 127 ),
... ... @@ -132,49 +134,54 @@ class _LessonPageView extends StatelessWidget {
132 134 );
133 135 });
134 136  
135   - Widget _itemTransCard(int index) => BlocBuilder<LessonBloc,LessonState>(
136   - builder: (context, state) {
137   - final bloc = BlocProvider.of<LessonBloc>(context);
138   - Matrix4 matrix4 = Matrix4.identity();
139   - if (index == bloc.currentPageIndex.floor()) {
140   - //当前的item
141   - double currScale = (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble();
142   - var currTrans = _cardHeight * (1 - currScale) / 2;
  137 + Widget _itemTransCard(int index) =>
  138 + BlocBuilder<ModuleBloc, ModuleState>(builder: (context, state) {
  139 + final bloc = BlocProvider.of<ModuleBloc>(context);
  140 + Matrix4 matrix4 = Matrix4.identity();
  141 + if (index == bloc.currentPageIndex.floor()) {
  142 + //当前的item
  143 + double currScale =
  144 + (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble();
  145 + var currTrans = _cardHeight * (1 - currScale) / 2;
143 146  
144   - matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
145   - ..setTranslationRaw(0.0, currTrans, 0.0);
146   - } else if (index == bloc.currentPageIndex.floor() + 1) {
147   - //右边的item
148   - var currScale = _scale + (bloc.currentPageIndex - index + 1) * (1 - _scale);
149   - var currTrans = _cardHeight * (1 - currScale) / 2;
  147 + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
  148 + ..setTranslationRaw(0.0, currTrans, 0.0);
  149 + } else if (index == bloc.currentPageIndex.floor() + 1) {
  150 + //右边的item
  151 + var currScale =
  152 + _scale + (bloc.currentPageIndex - index + 1) * (1 - _scale);
  153 + var currTrans = _cardHeight * (1 - currScale) / 2;
150 154  
151   - matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
152   - ..setTranslationRaw(0.0, currTrans, 0.0);
153   - } else if (index == bloc.currentPageIndex - 1) {
154   - //左边
155   - var currScale = (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble();
156   - var currTrans = _cardHeight * (1 - currScale) / 2;
  155 + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
  156 + ..setTranslationRaw(0.0, currTrans, 0.0);
  157 + } else if (index == bloc.currentPageIndex - 1) {
  158 + //左边
  159 + var currScale =
  160 + (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble();
  161 + var currTrans = _cardHeight * (1 - currScale) / 2;
157 162  
158   - matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
159   - ..setTranslationRaw(0.0, currTrans, 0.0);
160   - } else {
161   - //其他,不在屏幕显示的item
162   - matrix4 = Matrix4.diagonal3Values(1.0, _scale, 1.0)
163   - ..setTranslationRaw(0.0, _cardHeight * (1 - _scale) / 2, 0.0);
164   - }
165   - CourseModuleEntity? model = bloc.listData[index];
166   - return Transform(
167   - transform: matrix4,
168   - child: Padding(
169   - padding: const EdgeInsets.symmetric(horizontal: 10),
170   - child: LessonItemWidget(
171   - model: model,
172   - isSelected: bloc.currentPageIndex == index,
173   - onClickEvent: () {
174   - pushNamedAndRemoveUntil(AppRouteName.home, (route) => false,arguments: {'moduleId':model?.id});
175   - },
  163 + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
  164 + ..setTranslationRaw(0.0, currTrans, 0.0);
  165 + } else {
  166 + //其他,不在屏幕显示的item
  167 + matrix4 = Matrix4.diagonal3Values(1.0, _scale, 1.0)
  168 + ..setTranslationRaw(0.0, _cardHeight * (1 - _scale) / 2, 0.0);
  169 + }
  170 + CourseModuleEntity? model = bloc.listData[index];
  171 + return Transform(
  172 + transform: matrix4,
  173 + child: Padding(
  174 + padding: const EdgeInsets.symmetric(horizontal: 10),
  175 + child: ModuleItemWidget(
  176 + model: model,
  177 + isSelected: bloc.currentPageIndex == index,
  178 + onClickEvent: () {
  179 + pushNamedAndRemoveUntil(
  180 + AppRouteName.courseUnit, (route) => route.isFirst,
  181 + arguments: {'courseModuleEntity': model});
  182 + },
  183 + ),
176 184 ),
177   - ),
178   - );
179   - });
  185 + );
  186 + });
180 187 }
... ...
lib/pages/lessons/widgets/lesson_item_widget.dart renamed to lib/pages/module/widgets/module_item_widget.dart
... ... @@ -4,10 +4,10 @@ import &#39;package:wow_english/common/extension/string_extension.dart&#39;;
4 4 import 'package:wow_english/common/widgets/ow_image_widget.dart';
5 5 import 'package:wow_english/models/course_module_entity.dart';
6 6  
7   -import '../../home/courese_module_model.dart';
  7 +import '../../section/courese_module_model.dart';
8 8  
9   -class LessonItemWidget extends StatelessWidget {
10   - const LessonItemWidget({super.key, required this.isSelected, this.model, this.onClickEvent});
  9 +class ModuleItemWidget extends StatelessWidget {
  10 + const ModuleItemWidget({super.key, required this.isSelected, this.model, this.onClickEvent});
11 11 ///是否被选中
12 12 final bool isSelected;
13 13 final CourseModuleEntity? model;
... ...
lib/pages/moduleSelect/event.dart deleted
1   -abstract class ModuleSelectEvent {}
2   -
3   -class InitEvent extends ModuleSelectEvent {}
4 0 \ No newline at end of file
lib/pages/moduleSelect/state.dart deleted
1   -import '../../models/app_config_entity.dart';
2   -
3   -class ModuleSelectState {
4   - ModuleSelectState init() {
5   - return ModuleSelectState();
6   - }
7   -
8   - ModuleSelectState clone() {
9   - return ModuleSelectState();
10   - }
11   -}
12   -
13   -class UpdateDialogState extends ModuleSelectState {
14   -
15   - final AppConfigEntity appConfigEntity;
16   -
17   - final bool forceUpdate;
18   -
19   - UpdateDialogState(this.forceUpdate, this.appConfigEntity);
20   -}
lib/pages/home/bloc/home_bloc.dart renamed to lib/pages/section/bloc/section_bloc.dart
1 1 import 'package:flutter/cupertino.dart';
2 2 import 'package:flutter/foundation.dart';
3 3 import 'package:flutter_bloc/flutter_bloc.dart';
4   -import 'package:wow_english/common/request/dao/home_dao.dart';
  4 +import 'package:wow_english/common/request/dao/lesson_dao.dart';
5 5 import 'package:wow_english/common/request/exception.dart';
6   -import 'package:wow_english/models/course_entity.dart';
7 6 import 'package:wow_english/common/request/dao/listen_dao.dart';
8 7 import 'package:wow_english/models/course_process_entity.dart';
9 8 import 'package:wow_english/utils/loading.dart';
10 9 import 'package:wow_english/utils/toast_util.dart';
11 10  
12   -part 'home_event.dart';
13   -part 'home_state.dart';
  11 +import '../../../models/course_section_entity.dart';
  12 +import '../../../models/course_unit_entity.dart';
14 13  
15   -class HomeBloc extends Bloc<HomeEvent, HomeState> {
16   - final String? moduleId;
  14 +part 'section_event.dart';
  15 +part 'section_state.dart';
17 16  
18   - CourseEntity? _modelData;
  17 +class SectionBloc extends Bloc<SectionEvent, SectionState> {
19 18  
20   - CourseEntity? get modelData => _modelData;
  19 + CourseUnitEntity _courseUnitEntity;
  20 +
  21 + CourseUnitEntity get courseUnitEntity => _courseUnitEntity;
  22 +
  23 + CourseUnitDetail _courseUnitDetail;
  24 +
  25 + CourseUnitDetail get courseUnitDetail => _courseUnitDetail;
  26 +
  27 + List<CourseSectionEntity>? _courseSectionDatas;
  28 +
  29 + List<CourseSectionEntity>? get courseSectionDatas => _courseSectionDatas;
21 30  
22 31 CourseProcessEntity? _processEntity;
23 32  
24 33 CourseProcessEntity? get processEntity => _processEntity;
25 34  
26   - HomeBloc(this.moduleId) : super(HomeInitial()) {
  35 + SectionBloc(this._courseUnitEntity, this._courseUnitDetail) : super(LessonInitial()) {
27 36 on<RequestDataEvent>(_requestData);
28 37 on<RequestExitClassEvent>(_requestExitClass);
29 38 on<RequestEnterClassEvent>(_requestEnterClass);
30 39 on<RequestVideoLessonEvent>(_requestVideoLesson);
31 40 }
32 41  
33   - void _requestData(RequestDataEvent event, Emitter<HomeState> emitter) async {
  42 + void _requestData(RequestDataEvent event, Emitter<SectionState> emitter) async {
34 43 try {
35 44 await loading(() async {
36   - _modelData = await HomeDao.courseLesson(moduleId: moduleId ?? '');
37   - emitter(HomeDataLoadState());
  45 + _courseSectionDatas = await LessonDao.courseSection(courseUnitId: _courseUnitDetail.id!);
  46 + emitter(LessonDataLoadState());
38 47 });
39 48 } catch (e) {
40 49 if (e is ApiException) {
... ... @@ -43,7 +52,7 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; {
43 52 }
44 53 }
45 54  
46   - void _requestVideoLesson(RequestVideoLessonEvent event, Emitter<HomeState> emitter) async {
  55 + void _requestVideoLesson(RequestVideoLessonEvent event, Emitter<SectionState> emitter) async {
47 56 try {
48 57 await loading(() async {
49 58 _processEntity = await ListenDao.process(event.courseLessonId);
... ... @@ -57,7 +66,7 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; {
57 66 }
58 67  
59 68  
60   - void _requestEnterClass(RequestEnterClassEvent event,Emitter<HomeState> emitter) async {
  69 + void _requestEnterClass(RequestEnterClassEvent event,Emitter<SectionState> emitter) async {
61 70 try {
62 71 await loading(() async {
63 72 await ListenDao.enterClass(event.courseLessonId);
... ... @@ -70,7 +79,7 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; {
70 79 }
71 80 }
72 81  
73   - void _requestExitClass(RequestExitClassEvent event,Emitter<HomeState> emitter) async {
  82 + void _requestExitClass(RequestExitClassEvent event,Emitter<SectionState> emitter) async {
74 83 await ListenDao.exitClass(event.courseLessonId,event.currentStep,event.currentTime);
75 84 }
76 85 }
... ...
lib/pages/home/bloc/home_event.dart renamed to lib/pages/section/bloc/section_event.dart
1   -part of 'home_bloc.dart';
  1 +part of 'section_bloc.dart';
2 2  
3 3 @immutable
4   -abstract class HomeEvent {}
  4 +abstract class SectionEvent {}
5 5  
6   -class RequestDataEvent extends HomeEvent {}
  6 +class RequestDataEvent extends SectionEvent {}
7 7  
8 8 ///获取视频课程内容
9   -class RequestVideoLessonEvent extends HomeEvent {
  9 +class RequestVideoLessonEvent extends SectionEvent {
10 10 final String courseLessonId;
11 11 final int courseType;
12 12 RequestVideoLessonEvent(this.courseLessonId, this.courseType);
13 13 }
14 14  
15 15 ///进入课堂
16   -class RequestEnterClassEvent extends HomeEvent {
  16 +class RequestEnterClassEvent extends SectionEvent {
17 17 final String courseLessonId;
18 18 final int courseType;
19 19 RequestEnterClassEvent(this.courseLessonId,this.courseType);
20 20 }
21 21  
22 22 ///退出课堂
23   -class RequestExitClassEvent extends HomeEvent {
  23 +class RequestExitClassEvent extends SectionEvent {
24 24 final String courseLessonId;
25 25 final String currentStep;
26 26 final String currentTime;
... ...
lib/pages/home/bloc/home_state.dart renamed to lib/pages/section/bloc/section_state.dart
1   -part of 'home_bloc.dart';
  1 +part of 'section_bloc.dart';
2 2  
3 3 @immutable
4   -abstract class HomeState {}
  4 +abstract class SectionState {}
5 5  
6   -class HomeInitial extends HomeState {}
  6 +class LessonInitial extends SectionState {}
7 7  
8   -class HomeDataLoadState extends HomeState {}
  8 +class LessonDataLoadState extends SectionState {}
9 9  
10   -class RequestVideoLessonState extends HomeState {
  10 +class RequestVideoLessonState extends SectionState {
11 11 final String courseLessonId;
12 12 final int type;
13 13 RequestVideoLessonState(this.courseLessonId,this.type);
14 14 }
15 15  
16   -class RequestEnterClassState extends HomeState{
  16 +class RequestEnterClassState extends SectionState{
17 17 final String courseLessonId;
18 18 final int courseType;
19 19 RequestEnterClassState(this.courseLessonId,this.courseType);
... ...
lib/pages/home/courese_module_model.dart renamed to lib/pages/section/courese_module_model.dart
... ... @@ -2,9 +2,10 @@ import &#39;package:flutter/material.dart&#39;;
2 2  
3 3 class CourseModuleModel {
4 4 Color get color => getCourseColor();
5   - String get courseModuleTitle => getCourseModuleTitle();
6   - String get courseModuleLogo => getCoureseImageName();
7 5  
  6 + String get courseModuleTitle => getCourseModuleTitle();
  7 +
  8 + String get courseModuleLogo => getCoureseImageName();
8 9  
9 10 String course;
10 11  
... ... @@ -84,4 +85,4 @@ class CourseModuleModel {
84 85 }
85 86 return 'red_positive';
86 87 }
87   -}
88 88 \ No newline at end of file
  89 +}
... ...
lib/pages/home/home_page.dart renamed to lib/pages/section/section_page.dart
... ... @@ -3,60 +3,47 @@ import &#39;package:flutter_bloc/flutter_bloc.dart&#39;;
3 3 import 'package:flutter_screenutil/flutter_screenutil.dart';
4 4 import 'package:wow_english/common/core/user_util.dart';
5 5 import 'package:wow_english/common/extension/string_extension.dart';
6   -import 'package:wow_english/models/course_entity.dart';
7   -import 'package:wow_english/pages/home/widgets/home_bouns_item.dart';
8   -import 'package:wow_english/pages/home/widgets/home_tab_header_widget.dart';
9   -import 'package:wow_english/pages/home/widgets/home_vidoe_item.dart';
  6 +import 'package:wow_english/models/course_unit_entity.dart';
  7 +import 'package:wow_english/pages/section/widgets/home_video_item.dart';
  8 +import 'package:wow_english/pages/section/widgets/section_bouns_item.dart';
  9 +import 'package:wow_english/pages/section/widgets/section_header_widget.dart';
10 10 import 'package:wow_english/route/route.dart';
11 11 import 'package:wow_english/utils/toast_util.dart';
12 12  
13   -import 'bloc/home_bloc.dart';
  13 +import '../../models/course_section_entity.dart';
  14 +import 'bloc/section_bloc.dart';
14 15 import 'courese_module_model.dart';
15 16  
16   -class HomePage extends StatelessWidget {
17   - const HomePage({super.key, this.moduleId});
  17 +/// 环节列表页
  18 +class SectionPage extends StatelessWidget {
  19 + const SectionPage({super.key, required this.courseUnitEntity, required this.courseUnitDetail});
18 20  
19   - /// 模块id
20   - final String? moduleId;
  21 + final CourseUnitEntity courseUnitEntity;
  22 +
  23 + /// unitId
  24 + final CourseUnitDetail courseUnitDetail;
21 25  
22 26 @override
23 27 Widget build(BuildContext context) {
24 28 return BlocProvider(
25   - create: (context) => HomeBloc(moduleId)..add(RequestDataEvent()),
26   - child: _HomePageView(context),
  29 + create: (context) => SectionBloc(courseUnitEntity, courseUnitDetail)..add(RequestDataEvent()),
  30 + child: _SectionPageView(context),
27 31 );
28 32 }
29 33 }
30 34  
31   -class _HomePageView extends StatelessWidget {
32   -
33   - const _HomePageView(this.context);
  35 +class _SectionPageView extends StatelessWidget {
  36 + const _SectionPageView(this.context);
34 37  
35 38 final BuildContext context;
36 39  
37   - void _headerActionEvent(HeaderActionType type) {
38   - if (type == HeaderActionType.video) {
39   - pushNamed(AppRouteName.reAfter);
40   - } else if (type == HeaderActionType.phase) {
41   - pushNamed(AppRouteName.lesson);
42   - } else if (type == HeaderActionType.listen) {
43   - pushNamed(AppRouteName.listen);
44   - } else if (type == HeaderActionType.shop) {
45   - pushNamed(AppRouteName.shop);
46   - } else if (type == HeaderActionType.user) {
47   - pushNamed(AppRouteName.user);
48   - } else if (type == HeaderActionType.home) {
49   - Navigator.pop(context);
50   - }
51   - }
52   -
53 40 @override
54 41 Widget build(BuildContext context) {
55   - final bloc = BlocProvider.of<HomeBloc>(context);
56   - return BlocListener<HomeBloc, HomeState>(
  42 + final bloc = BlocProvider.of<SectionBloc>(context);
  43 + return BlocListener<SectionBloc, SectionState>(
57 44 listener: (context, state) {
58 45 if (state is RequestVideoLessonState) {
59   - final videoUrl = bloc.processEntity?.videos?.videoUrl??'';
  46 + final videoUrl = bloc.processEntity?.videos?.videoUrl ?? '';
60 47 var title = '';
61 48 if (state.type == 1) {
62 49 title = 'song';
... ... @@ -73,9 +60,13 @@ class _HomePageView extends StatelessWidget {
73 60 if (videoUrl.isEmpty || !videoUrl.contains('http')) {
74 61 return;
75 62 }
76   - pushNamed(AppRouteName.lookVideo,arguments: {'videoUrl':videoUrl,'title':title,'courseLessonId':state.courseLessonId}).then((value) {
  63 + pushNamed(AppRouteName.lookVideo, arguments: {
  64 + 'videoUrl': videoUrl,
  65 + 'title': title,
  66 + 'courseLessonId': state.courseLessonId
  67 + }).then((value) {
77 68 if (value != null) {
78   - Map<String,String> dataMap = value as Map<String,String>;
  69 + Map<String, String> dataMap = value as Map<String, String>;
79 70 bloc.add(RequestExitClassEvent(
80 71 dataMap['courseLessonId']!,
81 72 '0',
... ... @@ -87,35 +78,37 @@ class _HomePageView extends StatelessWidget {
87 78 }
88 79  
89 80 if (state is RequestEnterClassState) {
90   - if (state.courseType != 3 && state.courseType != 4) {///视频类型
  81 + if (state.courseType != 3 && state.courseType != 4) {
  82 + ///视频类型
91 83 ///获取视频课程内容
92   - bloc.add(RequestVideoLessonEvent(state.courseLessonId,state.courseType));
  84 + bloc.add(RequestVideoLessonEvent(
  85 + state.courseLessonId, state.courseType));
93 86 return;
94 87 }
95 88  
96   - if (state.courseType == 4) {//绘本
97   - pushNamed(AppRouteName.reading, arguments: {'courseLessonId':state.courseLessonId}).then((value) {
  89 + if (state.courseType == 4) {
  90 + //绘本
  91 + pushNamed(AppRouteName.reading,
  92 + arguments: {'courseLessonId': state.courseLessonId})
  93 + .then((value) {
98 94 if (value != null) {
99   - Map<String,String> dataMap = value as Map<String,String>;
  95 + Map<String, String> dataMap = value as Map<String, String>;
100 96 bloc.add(RequestExitClassEvent(
101   - dataMap['courseLessonId']!,
102   - dataMap['currentStep']!,
103   - '0'
104   - ));
  97 + dataMap['courseLessonId']!, dataMap['currentStep']!, '0'));
105 98 }
106 99 });
107 100 return;
108 101 }
109 102  
110   - if (state.courseType == 3) {//练习
111   - pushNamed(AppRouteName.topicPic,arguments: {'courseLessonId':state.courseLessonId}).then((value) {
  103 + if (state.courseType == 3) {
  104 + //练习
  105 + pushNamed(AppRouteName.topicPic,
  106 + arguments: {'courseLessonId': state.courseLessonId})
  107 + .then((value) {
112 108 if (value != null) {
113   - Map<String,String> dataMap = value as Map<String,String>;
  109 + Map<String, String> dataMap = value as Map<String, String>;
114 110 bloc.add(RequestExitClassEvent(
115   - dataMap['courseLessonId']!,
116   - dataMap['currentStep']!,
117   - '0'
118   - ));
  111 + dataMap['courseLessonId']!, dataMap['currentStep']!, '0'));
119 112 }
120 113 });
121 114 return;
... ... @@ -126,9 +119,9 @@ class _HomePageView extends StatelessWidget {
126 119 );
127 120 }
128 121  
129   - Widget _homeView() => BlocBuilder<HomeBloc, HomeState>(
130   - builder: (context, state) {
131   - final bloc = BlocProvider.of<HomeBloc>(context);
  122 + Widget _homeView() =>
  123 + BlocBuilder<SectionBloc, SectionState>(builder: (context, state) {
  124 + final bloc = BlocProvider.of<SectionBloc>(context);
132 125 return Scaffold(
133 126 body: Container(
134 127 color: Colors.white,
... ... @@ -136,19 +129,17 @@ class _HomePageView extends StatelessWidget {
136 129 child: Column(
137 130 mainAxisAlignment: MainAxisAlignment.spaceBetween,
138 131 children: [
139   - HomeTabHeaderWidget(
140   - entity: bloc.modelData,
141   - actionTap: (HeaderActionType type) {
142   - _headerActionEvent(type);
143   - },
144   - ),
  132 + SectionHeaderWidget(
  133 + title: bloc.courseUnitDetail.name,
  134 + courseModuleCode: bloc.courseUnitEntity.courseModuleCode),
145 135 Expanded(
146 136 child: ListView.builder(
147   - itemCount: bloc.modelData?.totalCourseLesson??0,
  137 + itemCount: bloc.courseSectionDatas?.length ?? 0,
148 138 scrollDirection: Axis.horizontal,
149 139 itemBuilder: (BuildContext context, int index) {
150   - CourseCourseLessons? data = bloc.modelData?.courseLessons?[index];
151   - if (data?.courseType == 5) {
  140 + CourseSectionEntity sectionData =
  141 + bloc.courseSectionDatas![index];
  142 + if (sectionData.courseType == 5) {
152 143 //彩蛋
153 144 return GestureDetector(
154 145 onTap: () {
... ... @@ -156,15 +147,17 @@ class _HomePageView extends StatelessWidget {
156 147 pushNamed(AppRouteName.login);
157 148 return;
158 149 }
159   - if (data!.lock!) {
  150 + if (sectionData.lock == true) {
160 151 showToast('当前课程暂未解锁');
161 152 return;
162 153 }
  154 +
163 155 ///进入课堂
164   - bloc.add(RequestEnterClassEvent(data.id!,data.courseType!));
  156 + bloc.add(RequestEnterClassEvent(
  157 + sectionData.id.toString(), sectionData.courseType));
165 158 },
166   - child: HomeBoundsItem(
167   - imageUrl: data?.coverUrl,
  159 + child: SectionBoundsItem(
  160 + imageUrl: sectionData.coverUrl,
168 161 ),
169 162 );
170 163 } else {
... ... @@ -174,16 +167,18 @@ class _HomePageView extends StatelessWidget {
174 167 pushNamed(AppRouteName.login);
175 168 return;
176 169 }
177   - if (data!.lock!) {
  170 + if (sectionData.lock == true) {
178 171 showToast('当前课程暂未解锁');
179 172 return;
180 173 }
  174 +
181 175 ///进入课堂
182   - bloc.add(RequestEnterClassEvent(data.id!,data.courseType!));
  176 + bloc.add(RequestEnterClassEvent(
  177 + sectionData.id.toString(), sectionData.courseType));
183 178 },
184   - child: HomeVideoItem(
185   - entity: bloc.modelData,
186   - lessons: data,
  179 + child: SectionVideoItem(
  180 + unitEntity: bloc.courseUnitEntity,
  181 + lessons: sectionData,
187 182 ),
188 183 );
189 184 }
... ... @@ -200,17 +195,26 @@ class _HomePageView extends StatelessWidget {
200 195 ),
201 196 Container(
202 197 decoration: BoxDecoration(
203   - color: CourseModuleModel(bloc.modelData?.courseModuleCode??'Phase-1').color,
  198 + color: CourseModuleModel(
  199 + bloc.courseUnitEntity.courseModuleCode ??
  200 + 'Phase-1')
  201 + .color,
204 202 borderRadius: BorderRadius.circular(14.5.r),
205 203 ),
206   - padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 24.w),
  204 + padding: EdgeInsets.symmetric(
  205 + vertical: 8.h, horizontal: 24.w),
207 206 child: Text(
208   - '${(bloc.modelData?.nowCourseLesson??0)}/${bloc.modelData?.totalCourseLesson??0}',
209   - style: TextStyle(color: Colors.white, fontSize: 12.sp),
  207 + '${(bloc.courseUnitEntity.nowStep ?? 0)}/${bloc.courseUnitEntity.total ?? 0}',
  208 + style: TextStyle(
  209 + color: Colors.white, fontSize: 12.sp),
210 210 ),
211 211 ),
212 212 Image.asset(
213   - CourseModuleModel(bloc.modelData?.courseModuleCode??'Phase-1').courseModuleLogo.assetPng,
  213 + CourseModuleModel(
  214 + bloc.courseUnitEntity.courseModuleCode ??
  215 + 'Phase-1')
  216 + .courseModuleLogo
  217 + .assetPng,
214 218 height: 47.h,
215 219 width: 80.w,
216 220 // color: Colors.red,
... ...
lib/pages/home/widgets/home_vidoe_item.dart renamed to lib/pages/section/widgets/home_video_item.dart
... ... @@ -2,15 +2,16 @@ import &#39;package:flutter/material.dart&#39;;
2 2 import 'package:flutter_screenutil/flutter_screenutil.dart';
3 3 import 'package:wow_english/common/extension/string_extension.dart';
4 4 import 'package:wow_english/common/widgets/ow_image_widget.dart';
5   -import 'package:wow_english/models/course_entity.dart';
6 5  
  6 +import '../../../models/course_section_entity.dart';
  7 +import '../../../models/course_unit_entity.dart';
7 8 import '../courese_module_model.dart';
8 9  
9   -class HomeVideoItem extends StatelessWidget {
10   - const HomeVideoItem({super.key, this.lessons, this.entity});
  10 +class SectionVideoItem extends StatelessWidget {
  11 + const SectionVideoItem({super.key, this.lessons, this.unitEntity});
11 12  
12   - final CourseEntity? entity;
13   - final CourseCourseLessons? lessons;
  13 + final CourseUnitEntity? unitEntity;
  14 + final CourseSectionEntity? lessons;
14 15  
15 16 @override
16 17 Widget build(BuildContext context) {
... ... @@ -50,7 +51,7 @@ class HomeVideoItem extends StatelessWidget {
50 51 width: 2,
51 52 color: const Color(0xFF140C10),
52 53 ),
53   - color: CourseModuleModel(entity?.courseModuleCode??'Phase-1').color,
  54 + color: CourseModuleModel(unitEntity?.courseModuleCode??'Phase-1').color,
54 55 borderRadius: BorderRadius.circular(6)
55 56 ),
56 57 padding: EdgeInsets.symmetric(horizontal: 10.w),
... ...
lib/pages/home/widgets/home_bouns_item.dart renamed to lib/pages/section/widgets/section_bouns_item.dart
... ... @@ -2,8 +2,8 @@ import &#39;package:flutter/cupertino.dart&#39;;
2 2 import 'package:flutter_screenutil/flutter_screenutil.dart';
3 3 import 'package:wow_english/common/widgets/ow_image_widget.dart';
4 4  
5   -class HomeBoundsItem extends StatelessWidget {
6   - const HomeBoundsItem({super.key, this.imageUrl});
  5 +class SectionBoundsItem extends StatelessWidget {
  6 + const SectionBoundsItem({super.key, this.imageUrl});
7 7  
8 8 final String? imageUrl;
9 9  
... ...
lib/pages/section/widgets/section_header_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:wow_english/common/extension/string_extension.dart';
  5 +import 'package:wow_english/pages/user/bloc/user_bloc.dart';
  6 +
  7 +import '../courese_module_model.dart';
  8 +
  9 +class SectionHeaderWidget extends StatelessWidget {
  10 + const SectionHeaderWidget({super.key, this.title, this.courseModuleCode});
  11 +
  12 + final String? title;
  13 +
  14 + final String? courseModuleCode;
  15 +
  16 + @override
  17 + Widget build(BuildContext context) {
  18 + return BlocBuilder<UserBloc, UserState>(
  19 + builder: (context, state) {
  20 + return Container(
  21 + height: 45,
  22 + width: double.infinity,
  23 + color: CourseModuleModel(courseModuleCode ?? 'Phase-1').color,
  24 + padding: EdgeInsets.symmetric(horizontal: 9.5.w),
  25 + child: Row(
  26 + children: [
  27 + ScreenUtil().bottomBarHeight.horizontalSpace,
  28 + GestureDetector(
  29 + onTap: () {
  30 + Navigator.pop(context);
  31 + },
  32 + child: Container(
  33 + alignment: Alignment.center,
  34 + child: Image.asset(
  35 + 'back_around'.assetPng,
  36 + height: 40.h,
  37 + width: 40.w,
  38 + ),
  39 + ),
  40 + ),
  41 + 20.horizontalSpace,
  42 + Expanded(
  43 + child: Text(
  44 + title ??
  45 + CourseModuleModel(courseModuleCode ?? 'Phase-1')
  46 + .courseModuleTitle,
  47 + textAlign: TextAlign.left,
  48 + style: const TextStyle(color: Colors.white, fontSize: 30.0),
  49 + )),
  50 + ScreenUtil().bottomBarHeight.horizontalSpace,
  51 + ],
  52 + ));
  53 + },
  54 + );
  55 + }
  56 +}
... ...
lib/pages/shopping/bloc.dart
... ... @@ -80,10 +80,9 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; {
80 80 await fluwx?.registerApi(appId: "wx365e5a79956a450a",
81 81 universalLink: "https://app-api.wowenglish.com.cn/app/");
82 82 wxPayResponseListener = (WeChatResponse response) {
83   - debugPrint("WqfPay wxPayResponseListener $response");
  83 + debugPrint("wxPayResponseListener $response");
84 84 if (response is WeChatPaymentResponse) {
85 85 if (response.errCode == 0) {
86   - debugPrint("WqfPay wxPayResponseListener response=${response.errCode}");
87 86 showToast("支付成功");
88 87 // Log.d("emitter isDone=${emitter.isDone}");
89 88  
... ...
lib/pages/tab/tab_page.dart
1 1 import 'package:flutter/material.dart';
2 2 import 'package:flutter_bloc/flutter_bloc.dart';
3   -import 'package:wow_english/pages/home/home_page.dart';
4   -import 'package:wow_english/pages/lessons/lesson_page.dart';
  3 +import 'package:wow_english/pages/module/module_page.dart';
5 4  
  5 +import '../unit/view.dart';
6 6 import 'blocs/tab_bloc.dart';
7 7  
8 8 class TabPage extends StatelessWidget {
9 9 const TabPage({super.key});
10 10  
11 11 final _pages =const <Widget>[
12   - HomePage(),
13   - LessonPage()
  12 + UnitPage(),
  13 + ModulePage()
14 14 ];
15 15  
16 16 final _tabIcons = const <Icon>[
... ...
lib/pages/unit/bloc.dart 0 → 100644
  1 +import 'package:bloc/bloc.dart';
  2 +import 'package:wow_english/pages/unit/widget/home_tab_header_widget.dart';
  3 +
  4 +import '../../common/request/dao/lesson_dao.dart';
  5 +import '../../common/request/exception.dart';
  6 +import '../../models/course_module_entity.dart';
  7 +import '../../models/course_unit_entity.dart';
  8 +import '../../route/route.dart';
  9 +import '../../utils/loading.dart';
  10 +import '../../utils/toast_util.dart';
  11 +import 'event.dart';
  12 +import 'state.dart';
  13 +
  14 +class UnitBloc extends Bloc<UnitEvent, UnitState> {
  15 +
  16 + CourseModuleEntity? _moduleEntity;
  17 +
  18 + CourseModuleEntity? get moduleEntity => _moduleEntity;
  19 +
  20 + CourseUnitEntity? _unitData;
  21 +
  22 + CourseUnitEntity? get unitData => _unitData;
  23 +
  24 +
  25 + UnitBloc(CourseModuleEntity? courseEntity) : super(UnitState().init()) {
  26 + on<RequestUnitDataEvent>(_requestData);
  27 + }
  28 +
  29 + void _requestData(RequestUnitDataEvent event, Emitter<UnitState> emitter) async {
  30 + try {
  31 + await loading(() async {
  32 + _unitData = await LessonDao.courseUnit(event.moduleId);
  33 + emitter(UnitDataLoadState());
  34 + });
  35 + } catch (e) {
  36 + if (e is ApiException) {
  37 + showToast(e.message ?? '请求失败,请检查网络连接');
  38 + }
  39 + }
  40 + }
  41 +
  42 + String? getCourseModuleCode() {
  43 + return _moduleEntity?.code ?? _unitData?.courseModuleCode;
  44 + }
  45 +
  46 + void headerActionEvent(HeaderActionType type) {
  47 + if (type == HeaderActionType.video) {
  48 + pushNamed(AppRouteName.reAfter);
  49 + } else if (type == HeaderActionType.phase) {
  50 + pushNamed(AppRouteName.courseModule);
  51 + } else if (type == HeaderActionType.listen) {
  52 + pushNamed(AppRouteName.listen);
  53 + } else if (type == HeaderActionType.shop) {
  54 + pushNamed(AppRouteName.shop);
  55 + } else if (type == HeaderActionType.user) {
  56 + pushNamed(AppRouteName.user);
  57 + }
  58 + }
  59 +}
... ...
lib/pages/unit/event.dart 0 → 100644
  1 +abstract class UnitEvent {}
  2 +
  3 +// 获取课程单元数据
  4 +class RequestUnitDataEvent extends UnitEvent {
  5 + final int? moduleId;
  6 +
  7 + RequestUnitDataEvent(this.moduleId);
  8 +}
... ...
lib/pages/unit/state.dart 0 → 100644
  1 +class UnitState {
  2 + UnitState init() {
  3 + return UnitState();
  4 + }
  5 +
  6 + UnitState clone() {
  7 + return UnitState();
  8 + }
  9 +}
  10 +
  11 +class UnitDataLoadState extends UnitState {}
0 12 \ No newline at end of file
... ...
lib/pages/unit/view.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:wow_english/pages/unit/state.dart';
  5 +import 'package:wow_english/pages/unit/widget/course_unit_item.dart';
  6 +import 'package:wow_english/pages/unit/widget/home_tab_header_widget.dart';
  7 +import 'package:wow_english/route/route.dart';
  8 +
  9 +import '../../models/course_module_entity.dart';
  10 +import '../../models/course_unit_entity.dart';
  11 +import '../../utils/toast_util.dart';
  12 +import 'bloc.dart';
  13 +import 'event.dart';
  14 +
  15 +// 课程列表页(多unit,参考口语星球的框或分割标志)
  16 +class UnitPage extends StatelessWidget {
  17 + const UnitPage({super.key, this.courseModuleEntity});
  18 +
  19 + /// 模块
  20 + final CourseModuleEntity? courseModuleEntity;
  21 +
  22 + @override
  23 + Widget build(BuildContext context) {
  24 + return BlocProvider(
  25 + create: (BuildContext context) => UnitBloc(courseModuleEntity)
  26 + ..add(RequestUnitDataEvent(courseModuleEntity?.id)),
  27 + child: Builder(builder: (context) => _buildPage(context)),
  28 + );
  29 + }
  30 +
  31 + Widget _buildPage(BuildContext context) {
  32 + return BlocBuilder<UnitBloc, UnitState>(builder: (context, state) {
  33 + final bloc = BlocProvider.of<UnitBloc>(context);
  34 + return Scaffold(
  35 + body: Container(
  36 + color: Colors.white,
  37 + child: Center(
  38 + child: Column(
  39 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  40 + children: [
  41 + HomeTabHeaderWidget(
  42 + courseModuleCode: bloc.getCourseModuleCode(),
  43 + actionTap: (HeaderActionType type) {
  44 + bloc.headerActionEvent(type);
  45 + },
  46 + ),
  47 + Expanded(
  48 + child: Container(
  49 + margin: EdgeInsets.symmetric(horizontal: 12.w),
  50 + child: ListView.builder(
  51 + itemCount:
  52 + bloc.unitData?.courseUnitVOList?.length ?? 0,
  53 + scrollDirection: Axis.horizontal,
  54 + itemBuilder: (BuildContext context, int index) {
  55 + CourseUnitDetail? data =
  56 + bloc.unitData?.courseUnitVOList?[index];
  57 + return GestureDetector(
  58 + onTap: () {
  59 + if (data.lock == true) {
  60 + showToast('当前unit暂未解锁');
  61 + return;
  62 + }
  63 +
  64 + pushNamed(AppRouteName.courseSection,
  65 + arguments: {
  66 + 'courseUnitEntity': bloc.unitData,
  67 + 'courseUnitDetail': data
  68 + });
  69 + },
  70 + child: CourseUnitItem(
  71 + unitEntity: bloc.unitData!,
  72 + unitLesson: data!,
  73 + ),
  74 + );
  75 + })),
  76 + ),
  77 + SafeArea(
  78 + child: Column(
  79 + children: [
  80 + 6.verticalSpace,
  81 + ],
  82 + ),
  83 + )
  84 + ],
  85 + ),
  86 + ),
  87 + ),
  88 + );
  89 + });
  90 + }
  91 +}
... ...
lib/pages/unit/widget/course_unit_item.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  3 +import 'package:wow_english/common/extension/string_extension.dart';
  4 +import 'package:wow_english/common/widgets/ow_image_widget.dart';
  5 +
  6 +import '../../../models/course_unit_entity.dart';
  7 +
  8 +class CourseUnitItem extends StatelessWidget {
  9 + const CourseUnitItem(
  10 + {super.key, required this.unitEntity, required this.unitLesson});
  11 +
  12 + final CourseUnitEntity unitEntity;
  13 + final CourseUnitDetail unitLesson;
  14 +
  15 + @override
  16 + Widget build(BuildContext context) {
  17 + return Padding(
  18 + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 24.h),
  19 + child: Container(
  20 + width: 165.w,
  21 + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 24.h),
  22 + decoration: BoxDecoration(
  23 + image: DecorationImage(
  24 + image: AssetImage('gendubeij'.assetPng), fit: BoxFit.fill),
  25 + ),
  26 + child: Column(
  27 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  28 + children: [
  29 + Expanded(
  30 + child: Container(
  31 + decoration: BoxDecoration(
  32 + border: Border.all(
  33 + width: 2,
  34 + color: const Color(0xFF140C10),
  35 + ),
  36 + borderRadius: BorderRadius.circular(6)),
  37 + child: OwImageWidget(
  38 + name: unitLesson.coverUrl ?? '',
  39 + fit: BoxFit.fitHeight,
  40 + ),
  41 + )),
  42 + 20.verticalSpace,
  43 + SizedBox(
  44 + height: 40.h,
  45 + child: Text(
  46 + unitLesson.name ?? '',
  47 + maxLines: 2,
  48 + overflow: TextOverflow.ellipsis,
  49 + style:
  50 + TextStyle(fontSize: 11.sp, color: const Color(0xFF140C10)),
  51 + ),
  52 + )
  53 + ],
  54 + ),
  55 + ),
  56 + );
  57 + }
  58 +}
... ...
lib/pages/home/widgets/home_tab_header_widget.dart renamed to lib/pages/unit/widget/home_tab_header_widget.dart
... ... @@ -5,8 +5,7 @@ import &#39;package:wow_english/common/extension/string_extension.dart&#39;;
5 5 import 'package:wow_english/pages/user/bloc/user_bloc.dart';
6 6  
7 7 import '../../../common/core/app_config_helper.dart';
8   -import '../../../models/course_entity.dart';
9   -import '../courese_module_model.dart';
  8 +import '../../section/courese_module_model.dart';
10 9  
11 10 enum HeaderActionType {
12 11 //视频跟读
... ... @@ -19,14 +18,13 @@ enum HeaderActionType {
19 18 shop,
20 19 //个人信息
21 20 user,
22   - //返回到(模块选择)首页
23   - home,
24 21 }
25 22  
26 23 class HomeTabHeaderWidget extends StatelessWidget {
27   - const HomeTabHeaderWidget({super.key, this.entity, this.actionTap});
  24 + const HomeTabHeaderWidget({super.key, this.courseModuleCode, this.actionTap});
  25 +
  26 + final String? courseModuleCode;
28 27  
29   - final CourseEntity? entity;
30 28 final Function(HeaderActionType type)? actionTap;
31 29  
32 30 @override
... ... @@ -37,16 +35,14 @@ class HomeTabHeaderWidget extends StatelessWidget {
37 35 height: 45,
38 36 width: double.infinity,
39 37 color:
40   - CourseModuleModel(entity?.courseModuleCode ?? 'Phase-1').color,
  38 + CourseModuleModel(courseModuleCode ?? 'Phase-1').color,
41 39 padding: EdgeInsets.symmetric(horizontal: 9.5.w),
42 40 child: Row(
43 41 children: [
44 42 ScreenUtil().bottomBarHeight.horizontalSpace,
45 43 GestureDetector(
46 44 onTap: () {
47   - if (actionTap != null) {
48   - actionTap!(HeaderActionType.home);
49   - }
  45 + Navigator.pop(context);
50 46 },
51 47 child: Container(
52 48 alignment: Alignment.center,
... ... @@ -96,7 +92,7 @@ class HomeTabHeaderWidget extends StatelessWidget {
96 92 20.horizontalSpace,
97 93 Expanded(
98 94 child: Text(
99   - CourseModuleModel(entity?.courseModuleCode ?? 'Phase-1')
  95 + CourseModuleModel(courseModuleCode ?? 'Phase-1')
100 96 .courseModuleTitle,
101 97 textAlign: TextAlign.left,
102 98 style: const TextStyle(color: Colors.white, fontSize: 30.0),
... ... @@ -122,8 +118,8 @@ class HomeTabHeaderWidget extends StatelessWidget {
122 118 }
123 119 },
124 120 icon: Image.asset('listen'.assetPng)),
125   - Visibility(
126   - visible: !AppConfigHelper.shouldHidePay(),
  121 + Offstage(
  122 + offstage: AppConfigHelper.shouldHidePay(),
127 123 child: IconButton(
128 124 onPressed: () {
129 125 if (actionTap != null) {
... ...
lib/pages/user/user_page.dart
... ... @@ -4,6 +4,7 @@ import &#39;package:flutter/services.dart&#39;;
4 4 import 'package:flutter/material.dart';
5 5 import 'package:flutter_bloc/flutter_bloc.dart';
6 6 import 'package:flutter_screenutil/flutter_screenutil.dart';
  7 +import 'package:wow_english/common/core/app_config_helper.dart';
7 8 import 'package:wow_english/common/core/app_consts.dart';
8 9 import 'package:wow_english/common/core/assets_const.dart';
9 10 import 'package:wow_english/common/core/user_util.dart';
... ... @@ -163,9 +164,8 @@ class _UserView extends StatelessWidget {
163 164 ),
164 165 ),
165 166 12.verticalSpace,
166   - // todo 为了过审,把测试账号兑换功能下掉
167 167 Offstage(
168   - offstage: UserUtil.getUser()?.phoneNum == '17730280759',
  168 + offstage: AppConfigHelper.shouldHidePay(),
169 169 child: OutlinedButton(
170 170 onPressed: () => pushNamed(AppRouteName.exLesson),
171 171 style: normalButtonStyle,
... ... @@ -175,7 +175,7 @@ class _UserView extends StatelessWidget {
175 175 )),
176 176 ),
177 177 Offstage(
178   - offstage: UserUtil.getUser()?.phoneNum == '17730280759',
  178 + offstage: AppConfigHelper.shouldHidePay(),
179 179 child: 12.verticalSpace,
180 180 ),
181 181 OutlinedButton(
... ...
lib/route/route.dart
... ... @@ -3,22 +3,21 @@ import &#39;package:flutter/material.dart&#39;;
3 3 import 'package:wow_english/app/splash_page.dart';
4 4 import 'package:wow_english/common/pages/wow_web_page.dart';
5 5 import 'package:wow_english/generated/json/base/json_convert_content.dart';
  6 +import 'package:wow_english/models/course_unit_entity.dart';
6 7 import 'package:wow_english/models/product_entity.dart';
7 8 import 'package:wow_english/pages/games/view.dart';
8   -import 'package:wow_english/pages/home/home_page.dart';
9   -import 'package:wow_english/pages/lessons/lesson_page.dart';
  9 +import 'package:wow_english/pages/home/view.dart';
10 10 import 'package:wow_english/pages/listen/listen_page.dart';
11 11 import 'package:wow_english/pages/login/forgetpwd/forget_password_home_page.dart';
12 12 import 'package:wow_english/pages/login/loginpage/login_page.dart';
13 13 import 'package:wow_english/pages/login/setpwd/set_pwd_page.dart';
14   -import 'package:wow_english/pages/moduleSelect/view.dart';
  14 +import 'package:wow_english/pages/module/module_page.dart';
15 15 import 'package:wow_english/pages/practice/topic_picture_page.dart';
16 16 import 'package:wow_english/pages/repeatafter/repeat_after_page.dart';
17 17 import 'package:wow_english/pages/repeataftercontent/repeat_after_content_page.dart';
18 18 import 'package:wow_english/pages/shop/exchane/exchange_lesson_page.dart';
19 19 import 'package:wow_english/pages/shop/exchangelist/exchange_lesson_list_page.dart';
20 20 import 'package:wow_english/pages/shop/home/shop_home_page.dart';
21   -import 'package:wow_english/pages/tab/tab_page.dart';
22 21 import 'package:wow_english/pages/user/information/user_information_page.dart';
23 22 import 'package:wow_english/pages/user/modify/modify_user_avatar_page.dart';
24 23 import 'package:wow_english/pages/user/modify/modify_user_information_page.dart';
... ... @@ -26,24 +25,28 @@ import &#39;package:wow_english/pages/user/setting/setting_page.dart&#39;;
26 25 import 'package:wow_english/pages/user/user_page.dart';
27 26 import 'package:wow_english/pages/video/lookvideo/look_video_page.dart';
28 27  
  28 +import '../models/course_module_entity.dart';
29 29 import '../pages/reading/reading_page.dart';
  30 +import '../pages/section/section_page.dart';
30 31 import '../pages/shopping/view.dart';
  32 +import '../pages/tab/tab_page.dart';
  33 +import '../pages/unit/view.dart';
31 34 import '../pages/user/setting/delete_account_page.dart';
32 35 import '../pages/user/setting/reback_page.dart';
33   -import '../utils/log_util.dart';
34 36  
35 37 class AppRouteName {
36 38 static const String splash = 'splash';
37 39 static const String login = 'login';
38   - static const String moduleSelect = 'moduleSelect';
39   - static const String games = 'games';
40 40 static const String home = 'home';
  41 + static const String games = 'games';
41 42 static const String fogPwd = 'fogPwd';
42 43  
43 44 /// 设置密码,修改密码;不要自己调用,使用[SetPassWordPage.push]方法,隐藏这种实现
44 45 //static const String setPwd = 'setPwd';
45 46 static const String webView = 'webView';
46   - static const String lesson = 'lesson';
  47 + static const String courseModule = 'courseModules';
  48 + static const String courseUnit = 'courseUnits';
  49 + static const String courseSection = 'courseSections';
47 50 static const String listen = 'listen';
48 51 static const String shop = 'shop';
49 52 static const String exLesson = 'exLesson';
... ... @@ -55,6 +58,7 @@ class AppRouteName {
55 58  
56 59 /// 用户详细信息页
57 60 static const String userInformation = 'userInformation';
  61 +
58 62 /// 修改用户头像
59 63 static const String userAvatar = 'userAvatar';
60 64  
... ... @@ -62,15 +66,19 @@ class AppRouteName {
62 66 //static const String userModifyInformation = 'userModifyInformation';
63 67 ///看视频
64 68 static const String lookVideo = 'lookVideo';
  69 +
65 70 ///绘本
66 71 static const String reading = 'reading';
  72 +
67 73 ///视频跟读详情
68 74 static const String readAfterContent = 'readAfterContent';
69 75  
70 76 ///设置
71 77 static const String setting = 'setting';
  78 +
72 79 ///注销账号
73 80 static const String deleteAccount = 'deleteAccount';
  81 +
74 82 ///帮助与反馈
75 83 static const String reBack = 'reBack';
76 84  
... ... @@ -93,25 +101,42 @@ class AppRouter {
93 101 transitionsBuilder: (_, __, ___, child) => child);
94 102 case AppRouteName.login:
95 103 // 是否默认密码登录,修改密码后跳登录页,优先密码登录体验好点
96   - final bool showPasswordPage = (settings.arguments as Map?)?.getOrNull('showPasswordPage') as bool? ?? false;
97   - return CupertinoPageRoute(builder: (_) => LoginPage(preferencesPasswordLogin: showPasswordPage));
98   - case AppRouteName.moduleSelect:
99   - return CupertinoPageRoute(builder: (_) => const ModuleSelectPage());
  104 + final bool showPasswordPage = (settings.arguments as Map?)
  105 + ?.getOrNull('showPasswordPage') as bool? ??
  106 + false;
  107 + return CupertinoPageRoute(
  108 + builder: (_) =>
  109 + LoginPage(preferencesPasswordLogin: showPasswordPage));
  110 + case AppRouteName.home:
  111 + return CupertinoPageRoute(builder: (_) => const HomePage());
100 112 case AppRouteName.games:
101 113 return CupertinoPageRoute(builder: (_) => const GamesPage());
102   - case AppRouteName.home:
103   - var moduleId = '';
  114 + case AppRouteName.fogPwd:
  115 + return CupertinoPageRoute(
  116 + builder: (_) => const ForgetPasswordHomePage());
  117 + case AppRouteName.courseModule:
  118 + return CupertinoPageRoute(builder: (_) => const ModulePage());
  119 + case AppRouteName.courseUnit:
  120 + CourseModuleEntity courseModuleEntity = CourseModuleEntity();
104 121 if (settings.arguments != null) {
105   - moduleId = (settings.arguments as Map)['moduleId'] as String;
  122 + courseModuleEntity = (settings.arguments as Map)
  123 + .getOrNull('courseModuleEntity') as CourseModuleEntity;
106 124 }
107 125 return CupertinoPageRoute(
108   - builder: (_) => HomePage(
109   - moduleId: moduleId,
110   - ));
111   - case AppRouteName.fogPwd:
112   - return CupertinoPageRoute(builder: (_) => const ForgetPasswordHomePage());
113   - case AppRouteName.lesson:
114   - return CupertinoPageRoute(builder: (_) => const LessonPage());
  126 + builder: (_) => UnitPage(courseModuleEntity: courseModuleEntity));
  127 + case AppRouteName.courseSection:
  128 + CourseUnitEntity courseUnitEntity = CourseUnitEntity();
  129 + CourseUnitDetail courseUnitDetail = CourseUnitDetail();
  130 + if (settings.arguments != null) {
  131 + courseUnitEntity = (settings.arguments as Map)
  132 + .getOrNull('courseUnitEntity') as CourseUnitEntity;
  133 + courseUnitDetail = (settings.arguments as Map)
  134 + .getOrNull('courseUnitDetail') as CourseUnitDetail;
  135 + }
  136 + return CupertinoPageRoute(
  137 + builder: (_) => SectionPage(
  138 + courseUnitEntity: courseUnitEntity,
  139 + courseUnitDetail: courseUnitDetail));
115 140 case AppRouteName.listen:
116 141 return CupertinoPageRoute(builder: (_) => const ListenPage());
117 142 case AppRouteName.shop:
... ... @@ -121,11 +146,13 @@ class AppRouter {
121 146 if (settings.arguments != null && settings.arguments is ProductEntity) {
122 147 productEntity = settings.arguments as ProductEntity;
123 148 }
124   - return CupertinoPageRoute(builder: (_) => ShoppingPage(productEntity: productEntity));
  149 + return CupertinoPageRoute(
  150 + builder: (_) => ShoppingPage(productEntity: productEntity));
125 151 case AppRouteName.exLesson:
126 152 return CupertinoPageRoute(builder: (_) => const ExchangeLessonPage());
127 153 case AppRouteName.exList:
128   - return CupertinoPageRoute(builder: (_) => const ExchangeLessonListPage());
  154 + return CupertinoPageRoute(
  155 + builder: (_) => const ExchangeLessonListPage());
129 156 case AppRouteName.reAfter:
130 157 return CupertinoPageRoute(builder: (_) => const RepeatAfterPage());
131 158 case AppRouteName.user:
... ... @@ -153,19 +180,22 @@ class AppRouter {
153 180 case AppRouteName.topicPic:
154 181 var courseLessonId = '';
155 182 if (settings.arguments != null) {
156   - courseLessonId = (settings.arguments as Map)['courseLessonId'] as String;
  183 + courseLessonId =
  184 + (settings.arguments as Map)['courseLessonId'] as String;
157 185 }
158   - return CupertinoPageRoute(builder: (_) => TopicPicturePage(courseLessonId: courseLessonId));
  186 + return CupertinoPageRoute(
  187 + builder: (_) => TopicPicturePage(courseLessonId: courseLessonId));
159 188 case AppRouteName.lookVideo:
160 189 final videoUrl = (settings.arguments as Map)['videoUrl'] as String;
161 190 final title = (settings.arguments as Map)['title'] as String?;
162   - final courseLessonId = (settings.arguments as Map)['courseLessonId'] as String?;
  191 + final courseLessonId =
  192 + (settings.arguments as Map)['courseLessonId'] as String?;
163 193 return CupertinoPageRoute(
164 194 builder: (_) => LookVideoPage(
165   - videoUrl: videoUrl,
166   - typeTitle: title,
167   - courseLessonId: courseLessonId,
168   - ));
  195 + videoUrl: videoUrl,
  196 + typeTitle: title,
  197 + courseLessonId: courseLessonId,
  198 + ));
169 199 /*case AppRouteName.setPwd:
170 200 case AppRouteName.setPwd:
171 201 phoneNum: phoneNum,
... ... @@ -174,7 +204,8 @@ class AppRouter {
174 204 ));*/
175 205 case AppRouteName.webView:
176 206 final urlStr = (settings.arguments as Map)['urlStr'] as String;
177   - final webViewTitle = (settings.arguments as Map)['webViewTitle'] as String;
  207 + final webViewTitle =
  208 + (settings.arguments as Map)['webViewTitle'] as String;
178 209 return CupertinoPageRoute(
179 210 builder: (_) => WowWebViewPage(
180 211 urlStr: urlStr,
... ... @@ -183,13 +214,16 @@ class AppRouter {
183 214 case AppRouteName.readAfterContent:
184 215 var videoFollowReadId = '';
185 216 if (settings.arguments != null) {
186   - videoFollowReadId = (settings.arguments as Map)['videoFollowReadId'] as String;
  217 + videoFollowReadId =
  218 + (settings.arguments as Map)['videoFollowReadId'] as String;
187 219 }
188   - return CupertinoPageRoute(builder: (_) => RepeatAfterContentPage(videoFollowReadId: videoFollowReadId));
  220 + return CupertinoPageRoute(
  221 + builder: (_) =>
  222 + RepeatAfterContentPage(videoFollowReadId: videoFollowReadId));
189 223 case AppRouteName.setting:
190   - return CupertinoPageRoute(builder: (_) => const SettingPage());
  224 + return CupertinoPageRoute(builder: (_) => const SettingPage());
191 225 case AppRouteName.deleteAccount:
192   - return CupertinoPageRoute(builder: (_) => const DeleteAccountPage());
  226 + return CupertinoPageRoute(builder: (_) => const DeleteAccountPage());
193 227 case AppRouteName.reBack:
194 228 return CupertinoPageRoute(builder: (_) => const ReBackPage());
195 229 case AppRouteName.tab:
... ... @@ -202,26 +236,34 @@ class AppRouter {
202 236 case AppRouteName.reading:
203 237 var courseLessonId = '';
204 238 if (settings.arguments != null) {
205   - courseLessonId = (settings.arguments as Map)['courseLessonId'] as String;
  239 + courseLessonId =
  240 + (settings.arguments as Map)['courseLessonId'] as String;
206 241 }
207   - return CupertinoPageRoute(builder: (_) => ReadingPage(courseLessonId: courseLessonId));
  242 + return CupertinoPageRoute(
  243 + builder: (_) => ReadingPage(courseLessonId: courseLessonId));
208 244 default:
209 245 return CupertinoPageRoute(
210   - builder: (_) => Scaffold(body: Center(child: Text('No route defined for ${settings.name}'))));
  246 + builder: (_) => Scaffold(
  247 + body: Center(
  248 + child: Text('No route defined for ${settings.name}'))));
211 249 }
212 250 }
213 251 }
214 252  
215 253 Future pushNamed(String routeName, {Object? arguments}) {
216   - return Navigator.of(AppRouter.context).pushNamed(routeName, arguments: arguments).then((value) {
  254 + return Navigator.of(AppRouter.context)
  255 + .pushNamed(routeName, arguments: arguments)
  256 + .then((value) {
217 257 return value;
218 258 });
219 259 }
220 260  
221   -Future pushNamedAndRemoveUntil(String routeName, RoutePredicate predicate, {Object? arguments}) {
222   - return Navigator.of(AppRouter.context).pushNamedAndRemoveUntil(routeName, predicate, arguments: arguments);
  261 +Future pushNamedAndRemoveUntil(String routeName, RoutePredicate predicate,
  262 + {Object? arguments}) {
  263 + return Navigator.of(AppRouter.context)
  264 + .pushNamedAndRemoveUntil(routeName, predicate, arguments: arguments);
223 265 }
224 266  
225 267 void popPage({dynamic data}) {
226   - Navigator.of(AppRouter.context).pop(data);
  268 + Navigator.of(AppRouter.context).pop(data);
227 269 }
... ...
pubspec.yaml
... ... @@ -16,7 +16,7 @@ publish_to: &#39;none&#39; # Remove this line if you wish to publish to pub.dev
16 16 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
17 17 # In Windows, build-name is used as the major, minor, and patch parts
18 18 # of the product and file versions while build-number is used as the build suffix.
19   -version: 1.0.3+2
  19 +version: 1.0.3+3
20 20  
21 21 environment:
22 22 sdk: '>=3.0.0 <4.0.0'
... ...