Commit cde7505edb1e27ff678d7900b19baf5fd5718324

Authored by 吴启风
1 parent 795fb23f

feat:应用内升级

android/app/build.gradle
@@ -88,10 +88,11 @@ dependencies { @@ -88,10 +88,11 @@ dependencies {
88 88
89 // sing sound 89 // sing sound
90 implementation 'com.singsound.library:evaluating:2.1.9' 90 implementation 'com.singsound.library:evaluating:2.1.9'
91 - implementation "com.google.code.gson:gson:2.9.0" 91 + implementation "com.google.code.gson:gson:2.10"
92 // 基础依赖包,必须要依赖 92 // 基础依赖包,必须要依赖
93 implementation 'com.geyifeng.immersionbar:immersionbar:3.2.2' 93 implementation 'com.geyifeng.immersionbar:immersionbar:3.2.2'
94 // kotlin扩展(可选) 94 // kotlin扩展(可选)
95 implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.2' 95 implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.2'
  96 + // coco2d游戏
96 implementation 'io.keyss.android.library:bjgame:1.0.3' 97 implementation 'io.keyss.android.library:bjgame:1.0.3'
97 } 98 }
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/methodChannels/GameMethodChannel.kt
1 package com.kouyuxingqiu.wow_english.methodChannels 1 package com.kouyuxingqiu.wow_english.methodChannels
2 2
3 import android.content.Intent 3 import android.content.Intent
4 -import android.content.Intent.getIntent  
5 import android.util.Log 4 import android.util.Log
6 -import androidx.core.content.ContextCompat.startActivity  
7 -import com.kouyuxingqiu.wow_english.singsound.SingEngineHelper  
8 -import com.kouyuxingqiu.wow_english.util.GlobalHandler  
9 import io.flutter.embedding.android.FlutterActivity 5 import io.flutter.embedding.android.FlutterActivity
10 import io.flutter.embedding.engine.FlutterEngine 6 import io.flutter.embedding.engine.FlutterEngine
11 import io.flutter.plugin.common.MethodChannel 7 import io.flutter.plugin.common.MethodChannel
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/methodChannels/SingSoungMethodChannel.kt
@@ -8,7 +8,6 @@ import com.kouyuxingqiu.wow_english.util.GlobalHandler @@ -8,7 +8,6 @@ import com.kouyuxingqiu.wow_english.util.GlobalHandler
8 import io.flutter.embedding.android.FlutterActivity 8 import io.flutter.embedding.android.FlutterActivity
9 import io.flutter.embedding.engine.FlutterEngine 9 import io.flutter.embedding.engine.FlutterEngine
10 import io.flutter.plugin.common.MethodChannel 10 import io.flutter.plugin.common.MethodChannel
11 -import org.json.JSONObject  
12 import java.lang.ref.WeakReference 11 import java.lang.ref.WeakReference
13 12
14 /** 13 /**
lib/common/core/app_config_helper.dart
@@ -10,14 +10,17 @@ import '../request/dao/system_dao.dart'; @@ -10,14 +10,17 @@ import '../request/dao/system_dao.dart';
10 10
11 class AppConfigHelper { 11 class AppConfigHelper {
12 12
13 - static AppConfigEntityEntity? configEntityEntity; 13 + static AppConfigEntity? configEntityEntity;
14 14
15 - static String versionCode = ''; 15 + static String _versionCode = '';
16 16
17 - /// 获取用户信息  
18 - static Future<Void?> getAppConfig() async { 17 + // 获取用户信息
  18 + static Future<AppConfigEntity?> getAppConfig() async {
  19 + if (configEntityEntity != null) {
  20 + return configEntityEntity;
  21 + }
19 configEntityEntity = await SystemDao.getAppConfig(); 22 configEntityEntity = await SystemDao.getAppConfig();
20 - return null; 23 + return configEntityEntity;
21 } 24 }
22 25
23 // 是否需要隐藏... 26 // 是否需要隐藏...
@@ -27,15 +30,15 @@ class AppConfigHelper { @@ -27,15 +30,15 @@ class AppConfigHelper {
27 30
28 // 获取app版本号 31 // 获取app版本号
29 static Future<String> getAppVersion() async { 32 static Future<String> getAppVersion() async {
30 - if (versionCode.isNotEmpty) {  
31 - return versionCode; 33 + if (_versionCode.isNotEmpty) {
  34 + return _versionCode;
32 } 35 }
33 PackageInfo packageInfo = await PackageInfo.fromPlatform(); 36 PackageInfo packageInfo = await PackageInfo.fromPlatform();
34 - String version = packageInfo.version; // 版本号  
35 - String buildNumber = packageInfo.buildNumber; // 构建号  
36 - versionCode = version; 37 + String versionName = packageInfo.version; // 版本号
  38 + String versionCode = packageInfo.buildNumber; // 构建号
  39 + _versionCode = versionCode;
37 40
38 - debugPrint('versionCode=$versionCode platForm=${Platform.operatingSystem}'); 41 + debugPrint('versionName=$versionName versionCode=$versionCode platForm=${Platform.operatingSystem}');
39 return versionCode; 42 return versionCode;
40 } 43 }
41 } 44 }
lib/common/core/user_util.dart
@@ -65,6 +65,7 @@ class UserUtil { @@ -65,6 +65,7 @@ class UserUtil {
65 65
66 // 是否有游戏权限 66 // 是否有游戏权限
67 static bool hasGamePermission() { 67 static bool hasGamePermission() {
  68 + debugPrint('hasGamePermission: ${_userEntity}');
68 return _userEntity?.valid ?? false; 69 return _userEntity?.valid ?? false;
69 } 70 }
70 71
lib/common/request/dao/system_dao.dart
@@ -4,7 +4,7 @@ import &#39;../request_client.dart&#39;; @@ -4,7 +4,7 @@ import &#39;../request_client.dart&#39;;
4 class SystemDao { 4 class SystemDao {
5 5
6 /// 获取配置信息 6 /// 获取配置信息
7 - static Future<AppConfigEntityEntity?> getAppConfig() async { 7 + static Future<AppConfigEntity?> getAppConfig() async {
8 return await requestClient.get(Apis.appConfig); 8 return await requestClient.get(Apis.appConfig);
9 } 9 }
10 } 10 }
lib/generated/json/app_config_entity.g.dart
1 import 'package:wow_english/generated/json/base/json_convert_content.dart'; 1 import 'package:wow_english/generated/json/base/json_convert_content.dart';
2 import 'package:wow_english/models/app_config_entity.dart'; 2 import 'package:wow_english/models/app_config_entity.dart';
3 3
4 -AppConfigEntityEntity $AppConfigEntityEntityFromJson( 4 +AppConfigEntity $AppConfigEntityEntityFromJson(
5 Map<String, dynamic> json) { 5 Map<String, dynamic> json) {
6 - final AppConfigEntityEntity appConfigEntityEntity = AppConfigEntityEntity(); 6 + final AppConfigEntity appConfigEntityEntity = AppConfigEntity();
7 final bool? androidForceUpdate = jsonConvert.convert<bool>( 7 final bool? androidForceUpdate = jsonConvert.convert<bool>(
8 json['androidForceUpdate']); 8 json['androidForceUpdate']);
9 if (androidForceUpdate != null) { 9 if (androidForceUpdate != null) {
@@ -50,7 +50,7 @@ AppConfigEntityEntity $AppConfigEntityEntityFromJson( @@ -50,7 +50,7 @@ AppConfigEntityEntity $AppConfigEntityEntityFromJson(
50 } 50 }
51 51
52 Map<String, dynamic> $AppConfigEntityEntityToJson( 52 Map<String, dynamic> $AppConfigEntityEntityToJson(
53 - AppConfigEntityEntity entity) { 53 + AppConfigEntity entity) {
54 final Map<String, dynamic> data = <String, dynamic>{}; 54 final Map<String, dynamic> data = <String, dynamic>{};
55 data['androidForceUpdate'] = entity.androidForceUpdate; 55 data['androidForceUpdate'] = entity.androidForceUpdate;
56 data['androidRecommendUpdate'] = entity.androidRecommendUpdate; 56 data['androidRecommendUpdate'] = entity.androidRecommendUpdate;
@@ -64,8 +64,8 @@ Map&lt;String, dynamic&gt; $AppConfigEntityEntityToJson( @@ -64,8 +64,8 @@ Map&lt;String, dynamic&gt; $AppConfigEntityEntityToJson(
64 return data; 64 return data;
65 } 65 }
66 66
67 -extension AppConfigEntityEntityExtension on AppConfigEntityEntity {  
68 - AppConfigEntityEntity copyWith({ 67 +extension AppConfigEntityEntityExtension on AppConfigEntity {
  68 + AppConfigEntity copyWith({
69 bool? androidForceUpdate, 69 bool? androidForceUpdate,
70 bool? androidRecommendUpdate, 70 bool? androidRecommendUpdate,
71 String? androidUpdatePackageUrl, 71 String? androidUpdatePackageUrl,
@@ -76,7 +76,7 @@ extension AppConfigEntityEntityExtension on AppConfigEntityEntity { @@ -76,7 +76,7 @@ extension AppConfigEntityEntityExtension on AppConfigEntityEntity {
76 String? noticeBeforePurchaseUrl, 76 String? noticeBeforePurchaseUrl,
77 String? safe, 77 String? safe,
78 }) { 78 }) {
79 - return AppConfigEntityEntity() 79 + return AppConfigEntity()
80 ..androidForceUpdate = androidForceUpdate ?? this.androidForceUpdate 80 ..androidForceUpdate = androidForceUpdate ?? this.androidForceUpdate
81 ..androidRecommendUpdate = androidRecommendUpdate ?? 81 ..androidRecommendUpdate = androidRecommendUpdate ??
82 this.androidRecommendUpdate 82 this.androidRecommendUpdate
lib/generated/json/base/json_convert_content.dart
@@ -155,9 +155,9 @@ class JsonConvert { @@ -155,9 +155,9 @@ class JsonConvert {
155 Map<String, dynamic> e) => 155 Map<String, dynamic> e) =>
156 AliyunOssUploadStsCallbackParam.fromJson(e)).toList() as M; 156 AliyunOssUploadStsCallbackParam.fromJson(e)).toList() as M;
157 } 157 }
158 - if (<AppConfigEntityEntity>[] is M) {  
159 - return data.map<AppConfigEntityEntity>((Map<String, dynamic> e) =>  
160 - AppConfigEntityEntity.fromJson(e)).toList() as M; 158 + if (<AppConfigEntity>[] is M) {
  159 + return data.map<AppConfigEntity>((Map<String, dynamic> e) =>
  160 + AppConfigEntity.fromJson(e)).toList() as M;
161 } 161 }
162 if (<CourseEntity>[] is M) { 162 if (<CourseEntity>[] is M) {
163 return data.map<CourseEntity>((Map<String, dynamic> e) => 163 return data.map<CourseEntity>((Map<String, dynamic> e) =>
@@ -236,7 +236,7 @@ class JsonConvertClassCollection { @@ -236,7 +236,7 @@ class JsonConvertClassCollection {
236 (AliyunOssUploadStsEntity).toString(): AliyunOssUploadStsEntity.fromJson, 236 (AliyunOssUploadStsEntity).toString(): AliyunOssUploadStsEntity.fromJson,
237 (AliyunOssUploadStsCallbackParam) 237 (AliyunOssUploadStsCallbackParam)
238 .toString(): AliyunOssUploadStsCallbackParam.fromJson, 238 .toString(): AliyunOssUploadStsCallbackParam.fromJson,
239 - (AppConfigEntityEntity).toString(): AppConfigEntityEntity.fromJson, 239 + (AppConfigEntity).toString(): AppConfigEntity.fromJson,
240 (CourseEntity).toString(): CourseEntity.fromJson, 240 (CourseEntity).toString(): CourseEntity.fromJson,
241 (CourseCourseLessons).toString(): CourseCourseLessons.fromJson, 241 (CourseCourseLessons).toString(): CourseCourseLessons.fromJson,
242 (CourseModuleEntity).toString(): CourseModuleEntity.fromJson, 242 (CourseModuleEntity).toString(): CourseModuleEntity.fromJson,
lib/models/app_config_entity.dart
@@ -5,7 +5,7 @@ import &#39;../generated/json/app_config_entity.g.dart&#39;; @@ -5,7 +5,7 @@ import &#39;../generated/json/app_config_entity.g.dart&#39;;
5 5
6 6
7 @JsonSerializable() 7 @JsonSerializable()
8 -class AppConfigEntityEntity { 8 +class AppConfigEntity {
9 9
10 // 安卓是否强制更新 10 // 安卓是否强制更新
11 bool? androidForceUpdate; 11 bool? androidForceUpdate;
@@ -17,14 +17,17 @@ class AppConfigEntityEntity { @@ -17,14 +17,17 @@ class AppConfigEntityEntity {
17 String? androidUpdatePackageUrl; 17 String? androidUpdatePackageUrl;
18 18
19 // 安卓当前版本号 19 // 安卓当前版本号
20 - int? androidVersion; 20 + late int androidVersion;
21 21
22 bool? iosForceUpdate; 22 bool? iosForceUpdate;
23 23
24 bool? iosRecommendUpdate; 24 bool? iosRecommendUpdate;
25 25
26 // ios版本 26 // ios版本
27 - int? iosVersion; 27 + late int iosVersion;
  28 +
  29 + // 更新说明
  30 + String? updatePackageDescription;
28 31
29 // 购前须知图片 32 // 购前须知图片
30 String? noticeBeforePurchaseUrl; 33 String? noticeBeforePurchaseUrl;
@@ -33,9 +36,9 @@ class AppConfigEntityEntity { @@ -33,9 +36,9 @@ class AppConfigEntityEntity {
33 String? safe; 36 String? safe;
34 37
35 38
36 - AppConfigEntityEntity(); 39 + AppConfigEntity();
37 40
38 - factory AppConfigEntityEntity.fromJson(Map<String, dynamic> json) => $AppConfigEntityEntityFromJson(json); 41 + factory AppConfigEntity.fromJson(Map<String, dynamic> json) => $AppConfigEntityEntityFromJson(json);
39 42
40 Map<String, dynamic> toJson() => $AppConfigEntityEntityToJson(this); 43 Map<String, dynamic> toJson() => $AppConfigEntityEntityToJson(this);
41 44
lib/pages/moduleSelect/bloc.dart
1 import 'package:bloc/bloc.dart'; 1 import 'package:bloc/bloc.dart';
  2 +import 'package:flutter/cupertino.dart';
  3 +import 'package:flutter/foundation.dart';
  4 +import 'package:wow_english/models/app_config_entity.dart';
2 5
  6 +import '../../common/core/app_config_helper.dart';
3 import 'event.dart'; 7 import 'event.dart';
4 import 'state.dart'; 8 import 'state.dart';
5 9
6 -  
7 class ModuleSelectBloc extends Bloc<ModuleSelectEvent, ModuleSelectState> { 10 class ModuleSelectBloc extends Bloc<ModuleSelectEvent, ModuleSelectState> {
8 -  
9 ModuleSelectBloc() : super(ModuleSelectState().init()) { 11 ModuleSelectBloc() : super(ModuleSelectState().init()) {
10 on<InitEvent>(_init); 12 on<InitEvent>(_init);
11 } 13 }
12 14
13 void _init(InitEvent event, Emitter<ModuleSelectState> emit) async { 15 void _init(InitEvent event, Emitter<ModuleSelectState> emit) async {
14 - emit(state.clone()); 16 + await _checkUpdate(emit);
  17 + debugPrint('WQF ModuleSelectBloc _init');
  18 + }
  19 +
  20 + Future<void> _checkUpdate(Emitter<ModuleSelectState> emit) async {
  21 + int localVersion = int.parse(await AppConfigHelper.getAppVersion());
  22 + AppConfigEntity? appConfigEntity = await AppConfigHelper.getAppConfig();
  23 + if (appConfigEntity == null) {
  24 + return;
  25 + }
  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 + }
  40 + }
15 } 41 }
16 } 42 }
lib/pages/moduleSelect/state.dart
  1 +import '../../models/app_config_entity.dart';
  2 +
1 class ModuleSelectState { 3 class ModuleSelectState {
2 ModuleSelectState init() { 4 ModuleSelectState init() {
3 return ModuleSelectState(); 5 return ModuleSelectState();
@@ -7,3 +9,12 @@ class ModuleSelectState { @@ -7,3 +9,12 @@ class ModuleSelectState {
7 return ModuleSelectState(); 9 return ModuleSelectState();
8 } 10 }
9 } 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/moduleSelect/view.dart
  1 +
  2 +import 'package:flutter/foundation.dart';
1 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
  4 +import 'package:flutter/services.dart';
  5 +import 'package:flutter_app_update/azhon_app_update.dart';
  6 +import 'package:flutter_app_update/update_model.dart';
2 import 'package:flutter_bloc/flutter_bloc.dart'; 7 import 'package:flutter_bloc/flutter_bloc.dart';
  8 +import 'package:url_launcher/url_launcher.dart';
3 import 'package:wow_english/common/core/app_config_helper.dart'; 9 import 'package:wow_english/common/core/app_config_helper.dart';
4 import 'package:wow_english/common/extension/string_extension.dart'; 10 import 'package:wow_english/common/extension/string_extension.dart';
5 import 'package:wow_english/pages/moduleSelect/state.dart'; 11 import 'package:wow_english/pages/moduleSelect/state.dart';
@@ -8,6 +14,7 @@ import &#39;package:wow_english/pages/user/bloc/user_bloc.dart&#39;; @@ -8,6 +14,7 @@ import &#39;package:wow_english/pages/user/bloc/user_bloc.dart&#39;;
8 14
9 import '../../common/core/user_util.dart'; 15 import '../../common/core/user_util.dart';
10 import '../../common/dialogs/show_dialog.dart'; 16 import '../../common/dialogs/show_dialog.dart';
  17 +import '../../models/app_config_entity.dart';
11 import 'bloc.dart'; 18 import 'bloc.dart';
12 import 'event.dart'; 19 import 'event.dart';
13 import 'package:flutter_screenutil/flutter_screenutil.dart'; 20 import 'package:flutter_screenutil/flutter_screenutil.dart';
@@ -19,7 +26,9 @@ class ModuleSelectPage extends StatelessWidget { @@ -19,7 +26,9 @@ class ModuleSelectPage extends StatelessWidget {
19 @override 26 @override
20 Widget build(BuildContext context) { 27 Widget build(BuildContext context) {
21 return BlocProvider( 28 return BlocProvider(
22 - create: (BuildContext context) => ModuleSelectBloc()..add(InitEvent()), 29 + create: (BuildContext context) =>
  30 + ModuleSelectBloc()
  31 + ..add(InitEvent()),
23 child: Builder(builder: (context) => _HomePageView()), 32 child: Builder(builder: (context) => _HomePageView()),
24 ); 33 );
25 } 34 }
@@ -33,79 +42,33 @@ class _HomePageView extends StatelessWidget { @@ -33,79 +42,33 @@ class _HomePageView extends StatelessWidget {
33 debugPrint('WQF ModuleSelectPage BlocListener state: $state'); 42 debugPrint('WQF ModuleSelectPage BlocListener state: $state');
34 }), 43 }),
35 BlocListener<ModuleSelectBloc, ModuleSelectState>( 44 BlocListener<ModuleSelectBloc, ModuleSelectState>(
36 - listener: (context, state) {}, 45 + listener: (context, state) {
  46 + if (state is UpdateDialogState) {
  47 + _showUpdateDialog(context, state.forceUpdate, state.appConfigEntity);
  48 + }
  49 + },
37 ), 50 ),
38 ], child: _homeView()); 51 ], child: _homeView());
39 } 52 }
40 53
41 - Widget _homeView() => BlocBuilder<ModuleSelectBloc, ModuleSelectState>( 54 + Widget _homeView() =>
  55 + BlocBuilder<ModuleSelectBloc, ModuleSelectState>(
42 builder: (context, state) { 56 builder: (context, state) {
43 - return Scaffold(  
44 - body: Container(  
45 - color: Colors.white,  
46 - child: Column(  
47 - children: [  
48 - const BaseHomeHeaderWidget(),  
49 - Expanded(  
50 - child: Center(  
51 - child: Row(  
52 - children: [  
53 - Expanded(  
54 - child: GestureDetector(  
55 - onTap: () {  
56 - if (UserUtil.isLogined()) {  
57 - pushNamed(AppRouteName.home);  
58 - } else {  
59 - pushNamed(AppRouteName.login);  
60 - }  
61 - },  
62 - child: Column(  
63 - mainAxisAlignment: MainAxisAlignment.center,  
64 - children: [  
65 - Stack(  
66 - alignment: AlignmentDirectional.center,  
67 - children: [  
68 - Image.asset('bg_frame_module'.assetPng,  
69 - width: 162.5.w, height: 203.5.h),  
70 - Center(  
71 - child: Image.asset(  
72 - 'pic_module_study'.assetPng,  
73 - width: 140.5.w,  
74 - height: 172.h),  
75 - )  
76 - ]),  
77 - 10.verticalSpace,  
78 - Image.asset('label_module_study'.assetPng,  
79 - width: 124.w, height: 34.h),  
80 - ],  
81 - ),  
82 - ),  
83 - ),  
84 - Expanded(  
85 - child: BlocBuilder<UserBloc, UserState>(  
86 - builder: (context, userState) {  
87 - final userBloc = BlocProvider.of<UserBloc>(context);  
88 - debugPrint(  
89 - 'WQF ModuleSelectPage BlocBuilder state: $userState');  
90 - return GestureDetector( 57 + return Scaffold(
  58 + body: Container(
  59 + color: Colors.white,
  60 + child: Column(
  61 + children: [
  62 + const BaseHomeHeaderWidget(),
  63 + Expanded(
  64 + child: Center(
  65 + child: Row(
  66 + children: [
  67 + Expanded(
  68 + child: GestureDetector(
91 onTap: () { 69 onTap: () {
92 - //如果没登录先登录  
93 if (UserUtil.isLogined()) { 70 if (UserUtil.isLogined()) {
94 - if (AppConfigHelper.shouldHidePay()) {  
95 - pushNamed(AppRouteName.games);  
96 - } else {  
97 - if (UserUtil.hasGamePermission()) {  
98 - pushNamed(AppRouteName.games);  
99 - } else {  
100 - showTwoActionDialog('提示', '忽略', '去续费',  
101 - '您的课程已到期,请快快续费继续学习吧!', leftTap: () {  
102 - popPage();  
103 - }, rightTap: () {  
104 - popPage();  
105 - pushNamed(AppRouteName.shop);  
106 - });  
107 - }  
108 - } 71 + pushNamed(AppRouteName.home);
109 } else { 72 } else {
110 pushNamed(AppRouteName.login); 73 pushNamed(AppRouteName.login);
111 } 74 }
@@ -118,27 +81,157 @@ class _HomePageView extends StatelessWidget { @@ -118,27 +81,157 @@ class _HomePageView extends StatelessWidget {
118 children: [ 81 children: [
119 Image.asset( 82 Image.asset(
120 'bg_frame_module'.assetPng, 83 'bg_frame_module'.assetPng,
121 - width: 162.5.w,  
122 - height: 203.5.h),  
123 - Image.asset(  
124 - 'pic_module_game'.assetPng,  
125 - width: 140.5.w,  
126 - height: 172.h) 84 + width: 162.5.w, height: 203.5.h),
  85 + Center(
  86 + child: Image.asset(
  87 + 'pic_module_study'.assetPng,
  88 + width: 140.5.w,
  89 + height: 172.h),
  90 + )
127 ]), 91 ]),
128 10.verticalSpace, 92 10.verticalSpace,
129 - Image.asset('label_module_game'.assetPng, 93 + Image.asset('label_module_study'.assetPng,
130 width: 124.w, height: 34.h), 94 width: 124.w, height: 34.h),
131 ], 95 ],
132 - ));  
133 - }), 96 + ),
  97 + ),
  98 + ),
  99 + Expanded(
  100 + child: BlocBuilder<UserBloc, UserState>(
  101 + builder: (context, userState) {
  102 + debugPrint(
  103 + 'WQF ModuleSelectPage BlocBuilder state: $userState');
  104 + return GestureDetector(
  105 + onTap: () {
  106 + //如果没登录先登录
  107 + if (UserUtil.isLogined()) {
  108 + if (AppConfigHelper
  109 + .shouldHidePay()) {
  110 + pushNamed(AppRouteName.games);
  111 + } else {
  112 + if (UserUtil
  113 + .hasGamePermission()) {
  114 + pushNamed(AppRouteName.games);
  115 + } else {
  116 + showTwoActionDialog(
  117 + '提示', '忽略', '去续费',
  118 + '您的课程已到期,请快快续费继续学习吧!',
  119 + leftTap: () {
  120 + popPage();
  121 + }, rightTap: () {
  122 + popPage();
  123 + pushNamed(AppRouteName.shop);
  124 + });
  125 + }
  126 + }
  127 + } else {
  128 + pushNamed(AppRouteName.login);
  129 + }
  130 + },
  131 + child: Column(
  132 + mainAxisAlignment: MainAxisAlignment
  133 + .center,
  134 + children: [
  135 + Stack(
  136 + alignment: AlignmentDirectional
  137 + .center,
  138 + children: [
  139 + Image.asset(
  140 + 'bg_frame_module'
  141 + .assetPng,
  142 + width: 162.5.w,
  143 + height: 203.5.h),
  144 + Image.asset(
  145 + 'pic_module_game'
  146 + .assetPng,
  147 + width: 140.5.w,
  148 + height: 172.h)
  149 + ]),
  150 + 10.verticalSpace,
  151 + Image.asset(
  152 + 'label_module_game'.assetPng,
  153 + width: 124.w, height: 34.h),
  154 + ],
  155 + ));
  156 + }),
  157 + ),
  158 + ],
134 ), 159 ),
135 - ],  
136 - ),  
137 - ),  
138 - )  
139 - ],  
140 - ), 160 + ),
  161 + )
  162 + ],
  163 + ),
  164 + ),
  165 + );
  166 + });
  167 +
  168 +
  169 + ///Flutter侧处理升级对话框
  170 + ///[forcedUpgrade] 是否强制升级
  171 + _showUpdateDialog(BuildContext context, bool forcedUpgrade,
  172 + AppConfigEntity appConfigEntity) {
  173 + showDialog(
  174 + context: context,
  175 + // 当我们点击除开对话框内容以外的区域是否关闭对话需用用到barrierDismissible参数 . 这个参数默认值是true ,但不能为null .
  176 + barrierDismissible: !forcedUpgrade,
  177 + builder: (BuildContext context) {
  178 + return WillPopScope(
  179 + onWillPop: () => Future.value(!forcedUpgrade),
  180 + child: AlertDialog(
  181 + title: const Text('发现新版本'),
  182 + content: Text(
  183 + appConfigEntity.updatePackageDescription ??
  184 + '修复了一些已知问题'),
  185 + actions: <Widget>[
  186 + TextButton(
  187 + child: Text(forcedUpgrade ? '退出' : '取消'),
  188 + onPressed: () =>
  189 + {
  190 + if (forcedUpgrade) {
  191 + SystemNavigator.pop()
  192 + } else
  193 + {
  194 + Navigator.of(context).pop()
  195 + }
  196 + },
  197 + ),
  198 + TextButton(
  199 + child: const Text('升级'),
  200 + onPressed: () async {
  201 + if (defaultTargetPlatform == TargetPlatform.iOS) {
  202 + _launchAppStore("6450870731");
  203 + return;
  204 + }
  205 + final String? apkUrl = appConfigEntity.androidUpdatePackageUrl;
  206 + if (apkUrl == null || apkUrl.isEmpty) {
  207 + return;
  208 + }
  209 + UpdateModel model = UpdateModel(
  210 + apkUrl,
  211 + "wowenglish.apk",
  212 + "ic_launcher",
  213 + '',
  214 + );
  215 + AzhonAppUpdate.update(model).then((value) =>
  216 + debugPrint('$value'));
  217 + if (!forcedUpgrade) {
  218 + Navigator.of(context).pop();
  219 + }
  220 + },
  221 + ),
  222 + ],
141 ), 223 ),
142 ); 224 );
143 - }); 225 + },
  226 + );
  227 + }
  228 +
  229 + void _launchAppStore(String appId) async {
  230 + final String url = 'https://apps.apple.com/cn/app/wow-english/id$appId';
  231 + if (await canLaunchUrl(Uri.parse(url))) {
  232 + await launchUrl(Uri.parse(url));
  233 + } else {
  234 + throw 'Could not launch $url';
  235 + }
  236 + }
144 } 237 }
pubspec.yaml
@@ -105,6 +105,8 @@ dependencies: @@ -105,6 +105,8 @@ dependencies:
105 flutter_oss_aliyun: ^6.2.7 105 flutter_oss_aliyun: ^6.2.7
106 # App信息 https://pub.dev/packages/package_info_plus 106 # App信息 https://pub.dev/packages/package_info_plus
107 package_info_plus: ^4.2.0 107 package_info_plus: ^4.2.0
  108 + # 应用内更新 https://pub-web.flutter-io.cn/packages/flutter_app_update
  109 + flutter_app_update: ^3.0.4
108 110
109 dev_dependencies: 111 dev_dependencies:
110 build_runner: ^2.4.4 112 build_runner: ^2.4.4