Commit d28ecdb0e9746ff584adf98a17b045aabbe9353f

Authored by xiaoyu
2 parents ed94c3db 2a9895f6

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

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 - implementation 'io.keyss.android.library:bjgame:1.0.3' 96 + // coco2d游戏
  97 + implementation 'io.keyss.android.library:steve_game:1.0.0'
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
@@ -45,7 +41,7 @@ class GameMethodChannel(activity: FlutterActivity, flutterEngine: FlutterEngine) @@ -45,7 +41,7 @@ class GameMethodChannel(activity: FlutterActivity, flutterEngine: FlutterEngine)
45 val gameId = call.argument<Int>("gameId") 41 val gameId = call.argument<Int>("gameId")
46 activity.startActivity( 42 activity.startActivity(
47 Intent(activity, AppActivity::class.java).apply { 43 Intent(activity, AppActivity::class.java).apply {
48 - putExtra("game", gameId) 44 + putExtra("gameId", gameId)
49 }) 45 })
50 result.success(true) 46 result.success(true)
51 } else { 47 } else {
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/app/splash_page.dart
@@ -16,6 +16,9 @@ import &#39;package:wow_english/route/route.dart&#39;; @@ -16,6 +16,9 @@ import &#39;package:wow_english/route/route.dart&#39;;
16 import 'package:wow_english/utils/log_util.dart'; 16 import 'package:wow_english/utils/log_util.dart';
17 import 'package:wow_english/utils/sp_util.dart'; 17 import 'package:wow_english/utils/sp_util.dart';
18 18
  19 +import '../common/core/app_consts.dart';
  20 +import '../common/widgets/webview_dialog.dart';
  21 +
19 class SplashPage extends StatelessWidget { 22 class SplashPage extends StatelessWidget {
20 const SplashPage({super.key}); 23 const SplashPage({super.key});
21 24
@@ -72,7 +75,33 @@ class _TransitionViewState extends State&lt;TransitionView&gt; { @@ -72,7 +75,33 @@ class _TransitionViewState extends State&lt;TransitionView&gt; {
72 } else { 75 } else {
73 pushNamedAndRemoveUntil(AppRouteName.login, (route) => false); 76 pushNamedAndRemoveUntil(AppRouteName.login, (route) => false);
74 }*/ 77 }*/
75 - pushNamedAndRemoveUntil(AppRouteName.moduleSelect, (route) => false); 78 + bool isAggreementAccepted = AppConfigHelper.getAgreementAccepted();
  79 + if (isAggreementAccepted) {
  80 + pushNamedAndRemoveUntil(AppRouteName.moduleSelect, (route) => false);
  81 + } else {
  82 + showDialog(
  83 + context: context,
  84 + barrierDismissible: false,
  85 + builder: (BuildContext context) {
  86 + return WillPopScope(
  87 + onWillPop: () => Future.value(false),
  88 + child: WebviewDialog(
  89 + title: "服务条款及隐私政策",
  90 + webUrl: AppConsts.userPrivacyPolicyUrl,
  91 + leftTap: () {
  92 + AppConfigHelper.saveAgreementAccepted(true);
  93 + pushNamedAndRemoveUntil(
  94 + AppRouteName.moduleSelect, (route) => false);
  95 + },
  96 + rightTap: () {
  97 + // 退出应用
  98 + SystemNavigator.pop();
  99 + },
  100 + ),
  101 + );
  102 + },
  103 + );
  104 + }
76 }); 105 });
77 } 106 }
78 107
lib/common/core/app_config_helper.dart
@@ -3,21 +3,26 @@ import &#39;dart:io&#39;; @@ -3,21 +3,26 @@ import &#39;dart:io&#39;;
3 3
4 import 'package:flutter/cupertino.dart'; 4 import 'package:flutter/cupertino.dart';
5 import 'package:package_info_plus/package_info_plus.dart'; 5 import 'package:package_info_plus/package_info_plus.dart';
  6 +import 'package:wow_english/common/core/sp_const.dart';
6 import 'package:wow_english/common/core/user_util.dart'; 7 import 'package:wow_english/common/core/user_util.dart';
7 8
8 import '../../models/app_config_entity.dart'; 9 import '../../models/app_config_entity.dart';
  10 +import '../../utils/sp_util.dart';
9 import '../request/dao/system_dao.dart'; 11 import '../request/dao/system_dao.dart';
10 12
11 class AppConfigHelper { 13 class AppConfigHelper {
12 14
13 - static AppConfigEntityEntity? configEntityEntity; 15 + static AppConfigEntity? configEntityEntity;
14 16
15 - static String versionCode = ''; 17 + static String _versionCode = '';
16 18
17 - /// 获取用户信息  
18 - static Future<Void?> getAppConfig() async { 19 + // 获取用户信息
  20 + static Future<AppConfigEntity?> getAppConfig() async {
  21 + if (configEntityEntity != null) {
  22 + return configEntityEntity;
  23 + }
19 configEntityEntity = await SystemDao.getAppConfig(); 24 configEntityEntity = await SystemDao.getAppConfig();
20 - return null; 25 + return configEntityEntity;
21 } 26 }
22 27
23 // 是否需要隐藏... 28 // 是否需要隐藏...
@@ -27,15 +32,27 @@ class AppConfigHelper { @@ -27,15 +32,27 @@ class AppConfigHelper {
27 32
28 // 获取app版本号 33 // 获取app版本号
29 static Future<String> getAppVersion() async { 34 static Future<String> getAppVersion() async {
30 - if (versionCode.isNotEmpty) {  
31 - return versionCode; 35 + if (_versionCode.isNotEmpty) {
  36 + return _versionCode;
32 } 37 }
33 PackageInfo packageInfo = await PackageInfo.fromPlatform(); 38 PackageInfo packageInfo = await PackageInfo.fromPlatform();
34 - String version = packageInfo.version; // 版本号  
35 - String buildNumber = packageInfo.buildNumber; // 构建号  
36 - versionCode = version; 39 + String versionName = packageInfo.version; // 版本号
  40 + String versionCode = packageInfo.buildNumber; // 构建号
  41 + _versionCode = versionCode;
37 42
38 - debugPrint('versionCode=$versionCode platForm=${Platform.operatingSystem}'); 43 + debugPrint('versionName=$versionName versionCode=$versionCode platForm=${Platform.operatingSystem}');
39 return versionCode; 44 return versionCode;
40 } 45 }
  46 +
  47 + static void saveAgreementAccepted(bool accepted) {
  48 + SpUtil.getInstance().setData(SpConst.prefsKeyAgreementAccepted, accepted);
  49 + }
  50 +
  51 + static bool getAgreementAccepted() {
  52 + return SpUtil.getInstance().get<bool>(SpConst.prefsKeyAgreementAccepted) ?? false;
  53 + }
  54 +
  55 + static void _clearUserData() {
  56 + SpUtil.getInstance().remove(SpConst.prefsKeyAgreementAccepted);
  57 + }
41 } 58 }
lib/common/core/sp_const.dart
1 class SpConst { 1 class SpConst {
2 static const String prefsKeyUserInfo = "key_user_info"; 2 static const String prefsKeyUserInfo = "key_user_info";
  3 +
  4 + static const String prefsKeyAgreementAccepted = "privacy_agreement_accepted";
3 } 5 }
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/shop_dao.dart
@@ -8,20 +8,20 @@ class ShopDao { @@ -8,20 +8,20 @@ class ShopDao {
8 } 8 }
9 9
10 ///创建订单 10 ///创建订单
11 - static Future createOrder(ProductEntity productEntity) async { 11 + static Future<Map<String, dynamic>?> createOrder(ProductEntity productEntity) async {
12 return await requestClient 12 return await requestClient
13 - .post<Map<String, dynamic>>(Apis.createOrder, data: {'courseComboId': productEntity.id}); 13 + .post<Map<String, dynamic>?>(Apis.createOrder, data: {'courseComboId': productEntity.id});
14 } 14 }
15 15
16 ///获取alipay支付订单信息 16 ///获取alipay支付订单信息
17 - static Future getAliPayToken(String orderNo) async { 17 + static Future<Map<String, dynamic>?> getAliPayToken(String orderNo) async {
18 return await requestClient 18 return await requestClient
19 - .post<Map<String, dynamic>>(Apis.getAliPayToken, data: {'orderNo': orderNo}); 19 + .post<Map<String, dynamic>?>(Apis.getAliPayToken, data: {'orderNo': orderNo});
20 } 20 }
21 21
22 ///获取weixin支付订单信息 22 ///获取weixin支付订单信息
23 - static Future getWxPayToken(String orderNo) async { 23 + static Future<Map<String, dynamic>?> getWxPayToken(String orderNo) async {
24 return await requestClient 24 return await requestClient
25 - .post<Map<String, dynamic>>(Apis.getWxPayToken, data: {'orderNo': orderNo}); 25 + .post<Map<String, dynamic>?>(Apis.getWxPayToken, data: {'orderNo': orderNo});
26 } 26 }
27 } 27 }
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/common/widgets/webview_dialog.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_easyloading/flutter_easyloading.dart';
  3 +import 'package:webview_flutter/webview_flutter.dart';
  4 +
  5 +class WebviewDialog extends StatelessWidget {
  6 + final String title;
  7 + final String webUrl;
  8 + final VoidCallback leftTap;
  9 + final VoidCallback rightTap;
  10 +
  11 + const WebviewDialog(
  12 + {super.key,
  13 + required this.title,
  14 + required this.webUrl,
  15 + required this.leftTap,
  16 + required this.rightTap});
  17 +
  18 + @override
  19 + Widget build(BuildContext context) {
  20 + return AlertDialog(
  21 + title: Center(
  22 + child: Text(title),
  23 + ),
  24 + content: SizedBox(
  25 + width: MediaQuery.of(context).size.height - 100,
  26 + height: MediaQuery.of(context).size.width - 100,
  27 + child: FutureBuilder(
  28 + // 异步方法
  29 + future: buildWebViewController(webUrl),
  30 + builder: (context, snapshot) {
  31 + // 等待状态显示的widget
  32 + if (snapshot.connectionState == ConnectionState.waiting) {
  33 + return const Center(
  34 + child: CircularProgressIndicator(),
  35 + );
  36 + // 错误时显示的widget
  37 + } else if (snapshot.hasError) {
  38 + return const Text('Error');
  39 + } else {
  40 + return snapshot.data ?? const Text('No data');
  41 + }
  42 + })),
  43 + actions: <Widget>[
  44 + TextButton(
  45 + child:
  46 + 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 + ),
  59 + ],
  60 + );
  61 + }
  62 +
  63 + Future<Widget> buildWebViewController(String url) async {
  64 + Widget res;
  65 + try {
  66 + WebViewController controller = WebViewController()
  67 + ..setJavaScriptMode(JavaScriptMode.unrestricted)
  68 + ..setBackgroundColor(const Color(0x00000000))
  69 + ..setNavigationDelegate(
  70 + NavigationDelegate(
  71 + onProgress: (int progress) {
  72 + // Update loading bar.
  73 + },
  74 + onPageStarted: (String url) {
  75 + EasyLoading.show();
  76 + },
  77 + onPageFinished: (String url) {
  78 + EasyLoading.dismiss();
  79 + },
  80 + onWebResourceError: (WebResourceError error) {
  81 + EasyLoading.showError(error.description);
  82 + },
  83 + onNavigationRequest: (NavigationRequest request) {
  84 + return NavigationDecision.navigate;
  85 + },
  86 + ),
  87 + );
  88 + await controller.loadRequest(Uri.parse(url));
  89 + res = WebViewWidget(controller: controller);
  90 + } catch (error) {
  91 + res = Text("加载失败:${error.toString()}");
  92 + debugPrint("WebViewController加载失败:${error.toString()}");
  93 + }
  94 + return res;
  95 + }
  96 +}
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/models/user_entity.dart
@@ -63,6 +63,11 @@ class UserEntity { @@ -63,6 +63,11 @@ class UserEntity {
63 : '女'; 63 : '女';
64 } 64 }
65 65
  66 + // 是否有游戏权限
  67 + bool hasGamePermission() {
  68 + return valid ?? false;
  69 + }
  70 +
66 UserEntity copyWith({ 71 UserEntity copyWith({
67 int? id, 72 int? id,
68 String? name, 73 String? name,
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';
6 import 'package:wow_english/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart'; 12 import 'package:wow_english/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart';
  13 +import 'package:wow_english/pages/user/bloc/user_bloc.dart';
7 14
8 import '../../common/core/user_util.dart'; 15 import '../../common/core/user_util.dart';
9 import '../../common/dialogs/show_dialog.dart'; 16 import '../../common/dialogs/show_dialog.dart';
  17 +import '../../models/app_config_entity.dart';
10 import 'bloc.dart'; 18 import 'bloc.dart';
11 import 'event.dart'; 19 import 'event.dart';
12 import 'package:flutter_screenutil/flutter_screenutil.dart'; 20 import 'package:flutter_screenutil/flutter_screenutil.dart';
@@ -18,7 +26,9 @@ class ModuleSelectPage extends StatelessWidget { @@ -18,7 +26,9 @@ class ModuleSelectPage extends StatelessWidget {
18 @override 26 @override
19 Widget build(BuildContext context) { 27 Widget build(BuildContext context) {
20 return BlocProvider( 28 return BlocProvider(
21 - create: (BuildContext context) => ModuleSelectBloc()..add(InitEvent()), 29 + create: (BuildContext context) =>
  30 + ModuleSelectBloc()
  31 + ..add(InitEvent()),
22 child: Builder(builder: (context) => _HomePageView()), 32 child: Builder(builder: (context) => _HomePageView()),
23 ); 33 );
24 } 34 }
@@ -27,106 +37,201 @@ class ModuleSelectPage extends StatelessWidget { @@ -27,106 +37,201 @@ class ModuleSelectPage extends StatelessWidget {
27 class _HomePageView extends StatelessWidget { 37 class _HomePageView extends StatelessWidget {
28 @override 38 @override
29 Widget build(BuildContext context) { 39 Widget build(BuildContext context) {
30 - final bloc = BlocProvider.of<ModuleSelectBloc>(context);  
31 - return BlocListener<ModuleSelectBloc, ModuleSelectState>(  
32 - listener: (context, state) {},  
33 - child: _homeView(),  
34 - ); 40 + return MultiBlocListener(listeners: [
  41 + BlocListener<UserBloc, UserState>(listener: (context, state) {
  42 + debugPrint('WQF ModuleSelectPage BlocListener state: $state');
  43 + }),
  44 + BlocListener<ModuleSelectBloc, ModuleSelectState>(
  45 + listener: (context, state) {
  46 + if (state is UpdateDialogState) {
  47 + _showUpdateDialog(context, state.forceUpdate, state.appConfigEntity);
  48 + }
  49 + },
  50 + ),
  51 + ], child: _homeView());
35 } 52 }
36 53
37 - Widget _homeView() => BlocBuilder<ModuleSelectBloc, ModuleSelectState>( 54 + Widget _homeView() =>
  55 + BlocBuilder<ModuleSelectBloc, ModuleSelectState>(
38 builder: (context, state) { 56 builder: (context, state) {
39 - final bloc = BlocProvider.of<ModuleSelectBloc>(context);  
40 - return Scaffold(  
41 - body: Container(  
42 - color: Colors.white,  
43 - child: Column(  
44 - children: [  
45 - const BaseHomeHeaderWidget(),  
46 - Expanded(  
47 - child: Center(  
48 - child: Row(  
49 - children: [  
50 - Expanded(  
51 - child: GestureDetector(  
52 - onTap: () {  
53 - if (UserUtil.isLogined()) {  
54 - pushNamed(AppRouteName.home);  
55 - } else {  
56 - pushNamed(AppRouteName.login);  
57 - }  
58 - },  
59 - child: Column(  
60 - mainAxisAlignment: MainAxisAlignment.center,  
61 - children: [  
62 - Stack(  
63 - alignment: AlignmentDirectional.center,  
64 - children: [  
65 - Image.asset('bg_frame_module'.assetPng,  
66 - width: 162.5.w, height: 203.5.h),  
67 - Center(  
68 - child: Image.asset(  
69 - 'pic_module_study'.assetPng,  
70 - width: 140.5.w,  
71 - height: 172.h),  
72 - )  
73 - ]),  
74 - 10.verticalSpace,  
75 - Image.asset('label_module_study'.assetPng,  
76 - width: 124.w, height: 34.h),  
77 - ],  
78 - ),  
79 - ),  
80 - ),  
81 - Expanded(  
82 - child: GestureDetector(  
83 - onTap: () {  
84 - //如果没登录先登录  
85 - if (UserUtil.isLogined()) {  
86 - if (AppConfigHelper.shouldHidePay()) {  
87 - pushNamed(AppRouteName.games); 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(
  69 + onTap: () {
  70 + if (UserUtil.isLogined()) {
  71 + pushNamed(AppRouteName.home);
88 } else { 72 } else {
89 - if (UserUtil.hasGamePermission()) {  
90 - pushNamed(AppRouteName.games);  
91 - } else {  
92 - showTwoActionDialog(  
93 - '提示', '忽略', '去续费',  
94 - '您的课程已到期,请快快续费继续学习吧!', leftTap: () {  
95 - popPage();  
96 - }, rightTap: () {  
97 - popPage();  
98 - pushNamed(AppRouteName.shop);  
99 - });  
100 - } 73 + pushNamed(AppRouteName.login);
101 } 74 }
102 - } else {  
103 - pushNamed(AppRouteName.login);  
104 - }  
105 - },  
106 - child: Column(  
107 - mainAxisAlignment: MainAxisAlignment.center,  
108 - children: [  
109 - Stack(  
110 - alignment: AlignmentDirectional.center,  
111 - children: [  
112 - Image.asset('bg_frame_module'.assetPng,  
113 - width: 162.5.w, height: 203.5.h),  
114 - Image.asset('pic_module_game'.assetPng,  
115 - width: 140.5.w, height: 172.h)  
116 - ]),  
117 - 10.verticalSpace,  
118 - Image.asset('label_module_game'.assetPng,  
119 - width: 124.w, height: 34.h),  
120 - ],  
121 - )), 75 + },
  76 + child: Column(
  77 + mainAxisAlignment: MainAxisAlignment.center,
  78 + children: [
  79 + Stack(
  80 + alignment: AlignmentDirectional.center,
  81 + children: [
  82 + Image.asset(
  83 + 'bg_frame_module'.assetPng,
  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 + )
  91 + ]),
  92 + 10.verticalSpace,
  93 + Image.asset('label_module_study'.assetPng,
  94 + width: 124.w, height: 34.h),
  95 + ],
  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 + ],
122 ), 159 ),
123 - ],  
124 - ),  
125 - ),  
126 - )  
127 - ],  
128 - ), 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 + ],
129 ), 223 ),
130 ); 224 );
131 - }); 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 + }
132 } 237 }
lib/pages/shopping/bloc.dart
@@ -36,6 +36,9 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; { @@ -36,6 +36,9 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; {
36 on<InitEvent>(_init); 36 on<InitEvent>(_init);
37 on<ChangePaymentChannelEvent>(_changePaymentChannel); 37 on<ChangePaymentChannelEvent>(_changePaymentChannel);
38 on<DoPayEvent>(_startPay); 38 on<DoPayEvent>(_startPay);
  39 + on<WxPaySuccessEvent>((event, emit) {
  40 + emit(PaySuccessState());
  41 + });
39 } 42 }
40 43
41 void _init(InitEvent event, Emitter<ShoppingState> emit) async { 44 void _init(InitEvent event, Emitter<ShoppingState> emit) async {
@@ -55,7 +58,6 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; { @@ -55,7 +58,6 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; {
55 58
56 void _startPay(DoPayEvent event, 59 void _startPay(DoPayEvent event,
57 Emitter<ShoppingState> emitter) async { 60 Emitter<ShoppingState> emitter) async {
58 - Log.d("开始支付 ${event.productEntity} ${event.paymentChannel}");  
59 //如果event.productEntity为空,中断流程并toast提示 61 //如果event.productEntity为空,中断流程并toast提示
60 if (event.productEntity == null) { 62 if (event.productEntity == null) {
61 showToast("商品信息为空"); 63 showToast("商品信息为空");
@@ -64,29 +66,31 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; { @@ -64,29 +66,31 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; {
64 final productEntitySafely = event.productEntity!; 66 final productEntitySafely = event.productEntity!;
65 try { 67 try {
66 await loading(() async { 68 await loading(() async {
67 - final Map<String, dynamic> orderInfo = await ShopDao.createOrder(productEntitySafely);  
68 - Log.d("orderInfo $orderInfo");  
69 - final String? orderNo = orderInfo.getOrNull("orderNo"); 69 + final Map<String, dynamic>? orderInfo = await ShopDao.createOrder(productEntitySafely);
  70 + final String? orderNo = orderInfo?.getOrNull("orderNo");
70 if (orderNo == null) { 71 if (orderNo == null) {
71 showToast("订单创建失败"); 72 showToast("订单创建失败");
72 return; 73 return;
73 } 74 }
74 - Log.d("orderNo $orderNo");  
75 75
76 if (event.paymentChannel == PaymentChannel.wechatPay) { 76 if (event.paymentChannel == PaymentChannel.wechatPay) {
77 if (_isWxPayListenerInitialized == false) { 77 if (_isWxPayListenerInitialized == false) {
78 _isWxPayListenerInitialized = true; 78 _isWxPayListenerInitialized = true;
79 fluwx = Fluwx(); 79 fluwx = Fluwx();
80 - fluwx?.registerApi(appId: "wx365e5a79956a450a", 80 + await fluwx?.registerApi(appId: "wx365e5a79956a450a",
81 universalLink: "https://app-api.wowenglish.com.cn/app/"); 81 universalLink: "https://app-api.wowenglish.com.cn/app/");
82 wxPayResponseListener = (WeChatResponse response) { 82 wxPayResponseListener = (WeChatResponse response) {
83 - Log.d("wxPayResponseListener $response"); 83 + debugPrint("WqfPay wxPayResponseListener $response");
84 if (response is WeChatPaymentResponse) { 84 if (response is WeChatPaymentResponse) {
85 if (response.errCode == 0) { 85 if (response.errCode == 0) {
86 - Log.d("wxPayResponseListener response=${response.errCode}"); 86 + debugPrint("WqfPay wxPayResponseListener response=${response.errCode}");
87 showToast("支付成功"); 87 showToast("支付成功");
88 // Log.d("emitter isDone=${emitter.isDone}"); 88 // Log.d("emitter isDone=${emitter.isDone}");
  89 +
  90 + /// 报错!_isCompleted emit was called after an event handler completed normally
89 // emitter(PaySuccessState()); 91 // emitter(PaySuccessState());
  92 +
  93 + add(WxPaySuccessEvent());
90 } else { 94 } else {
91 showToast("支付失败"); 95 showToast("支付失败");
92 } 96 }
@@ -103,10 +107,8 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; { @@ -103,10 +107,8 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; {
103 return; 107 return;
104 } 108 }
105 109
106 - final Map<String, dynamic> wxPayOrderInfo = await ShopDao.getWxPayToken(orderNo);  
107 - Log.d("wxPayOrderInfo=$wxPayOrderInfo type=${wxPayOrderInfo.runtimeType}");  
108 - final String? wxPayInfo = wxPayOrderInfo.getOrNull("appId");  
109 - if (wxPayInfo == null) { 110 + final Map<String, dynamic>? wxPayOrderInfo = await ShopDao.getWxPayToken(orderNo);
  111 + if (wxPayOrderInfo == null) {
110 showToast("微信订单创建失败"); 112 showToast("微信订单创建失败");
111 return; 113 return;
112 } 114 }
@@ -122,18 +124,18 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; { @@ -122,18 +124,18 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; {
122 sign: wxPayOrderInfo['sign'].toString(), 124 sign: wxPayOrderInfo['sign'].toString(),
123 )); 125 ));
124 } else { 126 } else {
125 - final Map<String, dynamic> aliPayOrderInfo = await ShopDao.getAliPayToken(orderNo);  
126 - Log.d("aliPayOrderInfo=$aliPayOrderInfo type=${aliPayOrderInfo.runtimeType}");  
127 - final String? aliPayInfo = aliPayOrderInfo.getOrNull("token"); 127 + final Map<String, dynamic>? aliPayOrderInfo = await ShopDao.getAliPayToken(orderNo);
  128 + debugPrint("aliPayOrderInfo=$aliPayOrderInfo type=${aliPayOrderInfo?.runtimeType}");
  129 + final String? aliPayInfo = aliPayOrderInfo?.getOrNull("token");
128 if (aliPayInfo == null) { 130 if (aliPayInfo == null) {
129 showToast("支付宝订单创建失败"); 131 showToast("支付宝订单创建失败");
130 return; 132 return;
131 } 133 }
132 - Log.d("aliPayInfo=$aliPayInfo"); 134 + debugPrint("aliPayInfo=$aliPayInfo");
133 ///打印aliPayOrderInfo的type 135 ///打印aliPayOrderInfo的type
134 Tobias tobias = Tobias(); 136 Tobias tobias = Tobias();
135 final Map aliPayResult = await tobias.pay(aliPayInfo); 137 final Map aliPayResult = await tobias.pay(aliPayInfo);
136 - Log.d("aliPayResult=$aliPayResult"); 138 + debugPrint("aliPayResult=$aliPayResult");
137 // 判断resultStatus 为9000则代表支付成功 139 // 判断resultStatus 为9000则代表支付成功
138 if (aliPayResult.getOrNull("resultStatus") == "9000") { 140 if (aliPayResult.getOrNull("resultStatus") == "9000") {
139 showToast("支付成功"); 141 showToast("支付成功");
lib/pages/shopping/event.dart
@@ -19,4 +19,8 @@ class DoPayEvent extends ShoppingEvent { @@ -19,4 +19,8 @@ class DoPayEvent extends ShoppingEvent {
19 final PaymentChannel paymentChannel; 19 final PaymentChannel paymentChannel;
20 20
21 DoPayEvent(this.productEntity, this.paymentChannel); 21 DoPayEvent(this.productEntity, this.paymentChannel);
22 -}  
23 \ No newline at end of file 22 \ No newline at end of file
  23 +}
  24 +
  25 +// 微信由于是异步回调方式通知支付状态,在异步回调里emitter(PaySuccessState())时报错
  26 +// !_isCompleted emit was called after an event handler completed normally
  27 +class WxPaySuccessEvent extends ShoppingEvent {}
24 \ No newline at end of file 28 \ No newline at end of file
lib/pages/shopping/view.dart
1 -import 'package:cached_network_image/cached_network_image.dart';  
2 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
3 import 'package:flutter_bloc/flutter_bloc.dart'; 2 import 'package:flutter_bloc/flutter_bloc.dart';
4 import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 import 'package:flutter_screenutil/flutter_screenutil.dart';
@@ -8,6 +7,7 @@ import &#39;package:wow_english/models/product_entity.dart&#39;; @@ -8,6 +7,7 @@ import &#39;package:wow_english/models/product_entity.dart&#39;;
8 import '../../common/core/assets_const.dart'; 7 import '../../common/core/assets_const.dart';
9 import '../../common/widgets/we_app_bar.dart'; 8 import '../../common/widgets/we_app_bar.dart';
10 import '../../utils/image_util.dart'; 9 import '../../utils/image_util.dart';
  10 +import '../user/bloc/user_bloc.dart';
11 import 'bloc.dart'; 11 import 'bloc.dart';
12 import 'event.dart'; 12 import 'event.dart';
13 import 'state.dart'; 13 import 'state.dart';
@@ -65,9 +65,11 @@ class _ShoppingView extends StatelessWidget { @@ -65,9 +65,11 @@ class _ShoppingView extends StatelessWidget {
65 @override 65 @override
66 Widget build(BuildContext context) { 66 Widget build(BuildContext context) {
67 final bloc = BlocProvider.of<ShoppingBloc>(context); 67 final bloc = BlocProvider.of<ShoppingBloc>(context);
  68 + final userBloc = BlocProvider.of<UserBloc>(context);
68 return BlocListener<ShoppingBloc, ShoppingState>( 69 return BlocListener<ShoppingBloc, ShoppingState>(
69 listener: (context, state) { 70 listener: (context, state) {
70 if (state is PaySuccessState) { 71 if (state is PaySuccessState) {
  72 + userBloc.add(PayStateChangeEvent());
71 Navigator.pop(context); 73 Navigator.pop(context);
72 } 74 }
73 }, 75 },
lib/pages/user/bloc/user_bloc.dart
@@ -12,12 +12,16 @@ part &#39;user_state.dart&#39;; @@ -12,12 +12,16 @@ part &#39;user_state.dart&#39;;
12 class UserBloc extends Bloc<UserEvent, UserState> { 12 class UserBloc extends Bloc<UserEvent, UserState> {
13 final TextEditingController modifyTextController = TextEditingController(); 13 final TextEditingController modifyTextController = TextEditingController();
14 int tempGender = 0; 14 int tempGender = 0;
  15 + UserEntity? _userEntityForPay;
  16 +
  17 + UserEntity? get userEntityForPay => _userEntityForPay;
15 18
16 UserBloc() : super(UserInitial()) { 19 UserBloc() : super(UserInitial()) {
17 on<UserLogout>(_logout); 20 on<UserLogout>(_logout);
18 on<UserDelete>(_deleteAccount); 21 on<UserDelete>(_deleteAccount);
19 on<UserUpdate>(_updateUser); 22 on<UserUpdate>(_updateUser);
20 on<UserUIUpdate>(_updateUIUser); 23 on<UserUIUpdate>(_updateUIUser);
  24 + on<PayStateChangeEvent>(_patStateChanged);
21 } 25 }
22 26
23 void _logout(UserLogout event, Emitter<UserState> emitter) async { 27 void _logout(UserLogout event, Emitter<UserState> emitter) async {
@@ -78,4 +82,17 @@ class UserBloc extends Bloc&lt;UserEvent, UserState&gt; { @@ -78,4 +82,17 @@ class UserBloc extends Bloc&lt;UserEvent, UserState&gt; {
78 Log.e('修改个人信息失败: $e'); 82 Log.e('修改个人信息失败: $e');
79 } 83 }
80 } 84 }
  85 +
  86 + //支付状态变化
  87 + void _patStateChanged(PayStateChangeEvent event, Emitter<UserState> emitter) async {
  88 + // 由于userInfo接口不会返回token,所以这里需要再次保存一下token
  89 + final token = UserUtil.getUser()?.token;
  90 + _userEntityForPay = await UserDao.getUserInfo();
  91 + if (_userEntityForPay == null) {
  92 + return;
  93 + }
  94 + _userEntityForPay?.token = token;
  95 + UserUtil.saveUser(_userEntityForPay);
  96 + emitter(UserPayStateChangedState());
  97 + }
81 } 98 }
lib/pages/user/bloc/user_event.dart
@@ -22,3 +22,5 @@ class UserUIUpdate extends UserEvent { @@ -22,3 +22,5 @@ class UserUIUpdate extends UserEvent {
22 22
23 UserUIUpdate(this.type); 23 UserUIUpdate(this.type);
24 } 24 }
  25 +
  26 +class PayStateChangeEvent extends UserEvent {}
25 \ No newline at end of file 27 \ No newline at end of file
lib/pages/user/bloc/user_state.dart
@@ -5,3 +5,5 @@ sealed class UserState {} @@ -5,3 +5,5 @@ sealed class UserState {}
5 class UserInitial extends UserState {} 5 class UserInitial extends UserState {}
6 6
7 class UserInfoUpdated extends UserState {} 7 class UserInfoUpdated extends UserState {}
  8 +
  9 +class UserPayStateChangedState extends UserState {}
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