Commit 6c23b545fdb0dbc00b8b36f597e8bcb5ae41ffb3

Authored by 吴启风
1 parent e26da9e0

fix: 修复友盟隐私合规初始化时机

android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/MainActivity.kt
@@ -11,8 +11,11 @@ import com.gyf.immersionbar.ImmersionBar @@ -11,8 +11,11 @@ import com.gyf.immersionbar.ImmersionBar
11 import com.kouyuxingqiu.wow_english.methodChannels.GameMethodChannel 11 import com.kouyuxingqiu.wow_english.methodChannels.GameMethodChannel
12 import com.kouyuxingqiu.wow_english.methodChannels.SingSoungMethodChannel 12 import com.kouyuxingqiu.wow_english.methodChannels.SingSoungMethodChannel
13 import com.umeng.commonsdk.UMConfigure 13 import com.umeng.commonsdk.UMConfigure
14 -import io.flutter.embedding.android.FlutterActivity  
15 import com.umeng.umcrash.UMCrash 14 import com.umeng.umcrash.UMCrash
  15 +import io.flutter.embedding.android.FlutterActivity
  16 +import io.flutter.embedding.engine.FlutterEngine
  17 +import io.flutter.plugin.common.BinaryMessenger
  18 +import io.flutter.plugin.common.MethodChannel
16 19
17 20
18 class MainActivity : FlutterActivity() { 21 class MainActivity : FlutterActivity() {
@@ -25,7 +28,11 @@ class MainActivity : FlutterActivity() { @@ -25,7 +28,11 @@ class MainActivity : FlutterActivity() {
25 SingSoungMethodChannel(this, it) 28 SingSoungMethodChannel(this, it)
26 GameMethodChannel(this, it) 29 GameMethodChannel(this, it)
27 } 30 }
28 - initUmeng() 31 + }
  32 +
  33 + override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
  34 + super.configureFlutterEngine(flutterEngine)
  35 + initUmengMethodChannel(flutterEngine.dartExecutor.binaryMessenger)
29 } 36 }
30 37
31 override fun onResume() { 38 override fun onResume() {
@@ -60,11 +67,26 @@ class MainActivity : FlutterActivity() { @@ -60,11 +67,26 @@ class MainActivity : FlutterActivity() {
60 /** 67 /**
61 * 友盟初始化 68 * 友盟初始化
62 */ 69 */
63 - private fun initUmeng() {  
64 - // 友盟集成测试阶段获取UMID  
65 -// android.util.Log.d("WQF UMConfigure", UMConfigure.getUMIDString(this))  
66 - // 在application.onCreate内配置各模块开关并预初始化SDK  
67 - // 重点关注:如果您还想采集Native 崩溃、ANR等日志可以参考下面设置 70 + private var isUmengPrepared = false
  71 +
  72 + private fun initUmengMethodChannel(binaryMessenger: BinaryMessenger) {
  73 + MethodChannel(binaryMessenger, "wow_english/umeng_method_channel")
  74 + .setMethodCallHandler { call, result ->
  75 + if (call.method == "prepareAfterPrivacyConsent") {
  76 + prepareUmengAfterPrivacyConsent()
  77 + result.success(true)
  78 + } else {
  79 + result.notImplemented()
  80 + }
  81 + }
  82 + }
  83 +
  84 + private fun prepareUmengAfterPrivacyConsent() {
  85 + if (isUmengPrepared) return
  86 +
  87 + // APM 合规要求:preInit、Crash 配置和正式 init 均只能在用户同意后执行。
  88 + UMConfigure.setLogEnabled(false)
  89 + UMConfigure.preInit(this, "663b66b0b3362515012f4ea5", "official")
68 UMCrash.initConfig(Bundle().apply { 90 UMCrash.initConfig(Bundle().apply {
69 putBoolean(UMCrash.KEY_ENABLE_CRASH_JAVA, true) 91 putBoolean(UMCrash.KEY_ENABLE_CRASH_JAVA, true)
70 putBoolean(UMCrash.KEY_ENABLE_CRASH_NATIVE, true) 92 putBoolean(UMCrash.KEY_ENABLE_CRASH_NATIVE, true)
@@ -75,8 +97,6 @@ class MainActivity : FlutterActivity() { @@ -75,8 +97,6 @@ class MainActivity : FlutterActivity() {
75 putBoolean(UMCrash.KEY_ENABLE_H5PAGE, false) 97 putBoolean(UMCrash.KEY_ENABLE_H5PAGE, false)
76 putBoolean(UMCrash.KEY_ENABLE_POWER, false) 98 putBoolean(UMCrash.KEY_ENABLE_POWER, false)
77 }) 99 })
78 - //UM日志打印  
79 - UMConfigure.setLogEnabled(false)  
80 - UMConfigure.preInit(this, "663b66b0b3362515012f4ea5", "official") 100 + isUmengPrepared = true
81 } 101 }
82 } 102 }
lib/app/splash_page.dart
@@ -23,7 +23,6 @@ import '../common/core/module_cache.dart'; @@ -23,7 +23,6 @@ import '../common/core/module_cache.dart';
23 import '../common/widgets/webview_dialog.dart'; 23 import '../common/widgets/webview_dialog.dart';
24 import '../common/utils/staticstring/userregister.dart'; 24 import '../common/utils/staticstring/userregister.dart';
25 25
26 -  
27 class SplashPage extends StatelessWidget { 26 class SplashPage extends StatelessWidget {
28 const SplashPage({super.key}); 27 const SplashPage({super.key});
29 28
@@ -43,10 +42,14 @@ class TransitionView extends StatefulWidget { @@ -43,10 +42,14 @@ class TransitionView extends StatefulWidget {
43 } 42 }
44 43
45 class _TransitionViewState extends State<TransitionView> { 44 class _TransitionViewState extends State<TransitionView> {
  45 + bool _isPrivacyDecisionInProgress = false;
  46 + bool _isInitializing = false;
  47 +
46 Future startTime() async { 48 Future startTime() async {
47 // 判断是否登录 49 // 判断是否登录
48 UserEntity? userEntity = UserUtil.getUser(); 50 UserEntity? userEntity = UserUtil.getUser();
49 - Log.d('当前模式:kDebugMode=$kDebugMode, kProfileMode=$kProfileMode, kReleaseMode=$kReleaseMode'); 51 + Log.d(
  52 + '当前模式:kDebugMode=$kDebugMode, kProfileMode=$kProfileMode, kReleaseMode=$kReleaseMode');
50 Log.d('当前API地址:${RequestConfig.baseUrl}'); 53 Log.d('当前API地址:${RequestConfig.baseUrl}');
51 //BuildContext context = Navigator.of(context).context; 54 //BuildContext context = Navigator.of(context).context;
52 //var currentPageName = ModalRoute.of(Navigator.of(AppRouter.context))?.settings.name; 55 //var currentPageName = ModalRoute.of(Navigator.of(AppRouter.context))?.settings.name;
@@ -63,89 +66,151 @@ class _TransitionViewState extends State&lt;TransitionView&gt; { @@ -63,89 +66,151 @@ class _TransitionViewState extends State&lt;TransitionView&gt; {
63 apartInMilliseconds = DateTime.now().difference(startTime).inMilliseconds; 66 apartInMilliseconds = DateTime.now().difference(startTime).inMilliseconds;
64 } 67 }
65 int duration = max(1500 - apartInMilliseconds, 0); 68 int duration = max(1500 - apartInMilliseconds, 0);
66 - Log.d('Splash getUserInfo 耗时:${apartInMilliseconds}ms, 最终duration=$duration'); 69 + Log.d(
  70 + 'Splash getUserInfo 耗时:${apartInMilliseconds}ms, 最终duration=$duration');
67 Timer(Duration(milliseconds: duration), () { 71 Timer(Duration(milliseconds: duration), () {
68 - /*if (userEntity != null) {  
69 - pushNamedAndRemoveUntil(AppRouteName.home, (route) => false);  
70 - } else {  
71 - pushNamedAndRemoveUntil(AppRouteName.login, (route) => false);  
72 - }*/  
73 - bool isAggreementAccepted = AppConfigHelper.getAgreementAccepted();  
74 - if (isAggreementAccepted) {  
75 - _initData();  
76 - } else {  
77 - showDialog(  
78 - context: context,  
79 - barrierDismissible: false,  
80 - builder: (BuildContext context) {  
81 - return PopScope(  
82 - canPop: false,  
83 - onPopInvoked: (didPop) {  
84 - Log.d('WQF isAggreementAccepted onPopInvoked didPop=$didPop');  
85 - },  
86 - child: child(),  
87 - ); 72 + pushNamedAndRemoveUntil(AppRouteName.home, (route) => false);
  73 + });
  74 + }
  75 +
  76 + void _showPrivacyDialog() {
  77 + showDialog(
  78 + context: context,
  79 + barrierDismissible: false,
  80 + builder: (BuildContext context) {
  81 + return PopScope(
  82 + canPop: false,
  83 + onPopInvoked: (didPop) {
  84 + Log.d('WQF privacy dialog onPopInvoked didPop=$didPop');
88 }, 85 },
  86 + child: child(),
89 ); 87 );
90 - }  
91 - }); 88 + },
  89 + );
92 } 90 }
93 91
94 Widget child() { 92 Widget child() {
95 if (Platform.isIOS) { 93 if (Platform.isIOS) {
96 return AlertDialog( 94 return AlertDialog(
97 - title: const Center(  
98 - child: Text("服务条款及隐私政策"),  
99 - ),  
100 - content: SizedBox( 95 + title: const Center(
  96 + child: Text("服务条款及隐私政策"),
  97 + ),
  98 + content: SizedBox(
101 width: MediaQuery.of(context).size.height, 99 width: MediaQuery.of(context).size.height,
102 height: MediaQuery.of(context).size.height, 100 height: MediaQuery.of(context).size.height,
103 - child: SingleChildScrollView( 101 + child: const SingleChildScrollView(
104 child: Text(UserRegisterString.userPrivacyPolicyStr), 102 child: Text(UserRegisterString.userPrivacyPolicyStr),
105 - ),),  
106 - actions: <Widget>[  
107 - Row(  
108 - mainAxisAlignment: MainAxisAlignment.spaceBetween,  
109 - children: [  
110 - TextButton(  
111 - child: const Text("同意并继续",  
112 - style: TextStyle(color: Color(0xFFFBB621))),  
113 - onPressed: () {  
114 - AppConfigHelper.saveAgreementAccepted(true);  
115 - _initData();  
116 - },  
117 - ),  
118 - TextButton(  
119 - child: const Text("不同意,退出应用"),  
120 - onPressed: () {  
121 - // 退出应用  
122 - AppConfigHelper.exitApp();  
123 - },  
124 - ),  
125 - ],  
126 - )  
127 - ],); 103 + ),
  104 + ),
  105 + actions: <Widget>[
  106 + Row(
  107 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  108 + children: [
  109 + TextButton(
  110 + onPressed: _acceptPrivacyAgreement,
  111 + child: const Text("同意并继续",
  112 + style: TextStyle(color: Color(0xFFFBB621))),
  113 + ),
  114 + TextButton(
  115 + onPressed: _rejectPrivacyAgreement,
  116 + child: const Text("不同意,退出应用"),
  117 + ),
  118 + ],
  119 + )
  120 + ],
  121 + );
128 } 122 }
129 return WebviewDialog( 123 return WebviewDialog(
130 - title: "服务条款及隐私政策",  
131 - webUrl: AppConsts.userPrivacyPolicyUrl,  
132 - leftButtonText: '同意并继续',  
133 - rightButtonText: '不同意,退出应用',  
134 - leftTap: () {  
135 - AppConfigHelper.saveAgreementAccepted(true);  
136 - _initData();  
137 - },  
138 - rightTap: () {  
139 - // 退出应用  
140 - AppConfigHelper.exitApp();  
141 - },  
142 - ); 124 + title: "服务条款及隐私政策",
  125 + webUrl: AppConsts.userPrivacyPolicyUrl,
  126 + leftButtonText: '同意并继续',
  127 + rightButtonText: '不同意,退出应用',
  128 + leftTap: _acceptPrivacyAgreement,
  129 + rightTap: _rejectPrivacyAgreement,
  130 + );
  131 + }
  132 +
  133 + Future<void> _acceptPrivacyAgreement() async {
  134 + if (_isPrivacyDecisionInProgress) return;
  135 + _isPrivacyDecisionInProgress = true;
  136 +
  137 + await AppConfigHelper.saveAgreementAccepted(true);
  138 + if (!mounted) return;
  139 +
  140 + // 先关闭隐私政策弹窗,再进行可能耗时的 SDK 和业务初始化。
  141 + Navigator.of(context, rootNavigator: true).pop();
  142 + await _startInitialization();
  143 + _isPrivacyDecisionInProgress = false;
  144 + }
  145 +
  146 + Future<void> _rejectPrivacyAgreement() async {
  147 + if (_isPrivacyDecisionInProgress) return;
  148 + _isPrivacyDecisionInProgress = true;
  149 +
  150 + // 显式撤销本地授权并等待写入完成,避免退出后残留 true。
  151 + await AppConfigHelper.saveAgreementAccepted(false);
  152 + AppConfigHelper.exitApp();
  153 + }
  154 +
  155 + Future<void> _startInitialization() async {
  156 + if (_isInitializing) return;
  157 + _isInitializing = true;
  158 + try {
  159 + await _initData();
  160 + } catch (error, stackTrace) {
  161 + Log.d('Splash初始化失败: $error\n$stackTrace');
  162 + _isInitializing = false;
  163 + if (mounted) {
  164 + _showInitializationErrorDialog();
  165 + }
  166 + }
  167 + }
  168 +
  169 + void _showInitializationErrorDialog() {
  170 + showDialog(
  171 + context: context,
  172 + barrierDismissible: false,
  173 + builder: (dialogContext) => AlertDialog(
  174 + title: const Text('温馨提示'),
  175 + content: const Text('初始化失败,请检查网络后重试'),
  176 + actions: [
  177 + const TextButton(
  178 + onPressed: AppConfigHelper.exitApp,
  179 + child: Text('退出'),
  180 + ),
  181 + TextButton(
  182 + onPressed: () {
  183 + Navigator.of(dialogContext).pop();
  184 + _startInitialization();
  185 + },
  186 + child: const Text('重试'),
  187 + ),
  188 + ],
  189 + ),
  190 + );
143 } 191 }
144 192
145 ///初始化数据 193 ///初始化数据
146 - void _initData() {  
147 - UmengCommonSdk.initCommon("663b66b0b3362515012f4ea5", "663b66ecf32cc41105ae74b7", "official");  
148 - pushNamedAndRemoveUntil(AppRouteName.home, (route) => false); 194 + Future<void> _initData() async {
  195 + // 用户同意后,先准备 Native APM,再正式初始化 Common SDK。
  196 + if (Platform.isAndroid) {
  197 + const MethodChannel umengMethodChannel =
  198 + MethodChannel('wow_english/umeng_method_channel');
  199 + await umengMethodChannel.invokeMethod('prepareAfterPrivacyConsent');
  200 + }
  201 + // Android 插件执行 initCommon 后不会回调 MethodChannel result,返回的
  202 + // Future 永远不结束,因此这里只触发初始化,不能 await。
  203 + unawaited(UmengCommonSdk.initCommon(
  204 + "663b66b0b3362515012f4ea5", "663b66ecf32cc41105ae74b7", "official")
  205 + .catchError((error, stackTrace) {
  206 + Log.d('友盟Common SDK初始化失败: $error\n$stackTrace');
  207 + }));
  208 + await AudioPlayerUtil.getInstance()
  209 + .playAudio(AudioPlayerUtilType.welcomeToWow)
  210 + .timeout(const Duration(seconds: 10), onTimeout: () {
  211 + Log.d('欢迎音频播放超时,继续进入首页');
  212 + });
  213 + await startTime();
149 } 214 }
150 215
151 Future<void> fetchNecessaryData(String userToken, 216 Future<void> fetchNecessaryData(String userToken,
@@ -164,6 +229,12 @@ class _TransitionViewState extends State&lt;TransitionView&gt; { @@ -164,6 +229,12 @@ class _TransitionViewState extends State&lt;TransitionView&gt; {
164 completer.complete(); 229 completer.complete();
165 } catch (e) { 230 } catch (e) {
166 debugPrint('Splash获取用户信息异常:$e'); 231 debugPrint('Splash获取用户信息异常:$e');
  232 + if (!mounted) {
  233 + if (!completer.isCompleted) {
  234 + completer.complete();
  235 + }
  236 + return;
  237 + }
167 //异常的话弹窗提示重试 238 //异常的话弹窗提示重试
168 showDialog( 239 showDialog(
169 context: context, 240 context: context,
@@ -219,17 +290,26 @@ class _TransitionViewState extends State&lt;TransitionView&gt; { @@ -219,17 +290,26 @@ class _TransitionViewState extends State&lt;TransitionView&gt; {
219 changeDevice(); 290 changeDevice();
220 await SpUtil.preInit(); 291 await SpUtil.preInit();
221 ModuleCache.instance.init(); 292 ModuleCache.instance.init();
222 - await AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.welcomeToWow);  
223 - startTime(); 293 + if (AppConfigHelper.getAgreementAccepted()) {
  294 + await _startInitialization();
  295 + } else {
  296 + WidgetsBinding.instance.addPostFrameCallback((_) {
  297 + if (mounted) {
  298 + _showPrivacyDialog();
  299 + }
  300 + });
  301 + }
224 } 302 }
225 303
226 void changeDevice() async { 304 void changeDevice() async {
227 ///设置设备默认方向 305 ///设置设备默认方向
228 WidgetsFlutterBinding.ensureInitialized(); 306 WidgetsFlutterBinding.ensureInitialized();
229 if (Platform.isIOS) { 307 if (Platform.isIOS) {
230 - await LimitingDirectionCsx.setScreenDirection(DeviceDirectionMask.Landscape); 308 + await LimitingDirectionCsx.setScreenDirection(
  309 + DeviceDirectionMask.Landscape);
231 } else { 310 } else {
232 - await SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]); 311 + await SystemChrome.setPreferredOrientations(
  312 + [DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
233 } 313 }
234 await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); 314 await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
235 } 315 }
lib/common/core/app_config_helper.dart
1 import 'dart:io'; 1 import 'dart:io';
2 2
3 -import 'package:flutter/cupertino.dart';  
4 import 'package:flutter/foundation.dart'; 3 import 'package:flutter/foundation.dart';
5 import 'package:flutter/services.dart'; 4 import 'package:flutter/services.dart';
6 import 'package:package_info_plus/package_info_plus.dart'; 5 import 'package:package_info_plus/package_info_plus.dart';
@@ -11,7 +10,6 @@ import &#39;../../utils/sp_util.dart&#39;; @@ -11,7 +10,6 @@ import &#39;../../utils/sp_util.dart&#39;;
11 import '../request/dao/system_dao.dart'; 10 import '../request/dao/system_dao.dart';
12 11
13 class AppConfigHelper { 12 class AppConfigHelper {
14 -  
15 static const String _prefsKeyAgreementAccepted = "privacy_agreement_accepted"; 13 static const String _prefsKeyAgreementAccepted = "privacy_agreement_accepted";
16 14
17 static AppConfigEntity? configEntityEntity; 15 static AppConfigEntity? configEntityEntity;
@@ -22,7 +20,7 @@ class AppConfigHelper { @@ -22,7 +20,7 @@ class AppConfigHelper {
22 static bool checkedUpdate = false; 20 static bool checkedUpdate = false;
23 21
24 // 获取用户信息 22 // 获取用户信息
25 - static Future<AppConfigEntity?> getAppConfig({ bool forceSync = false }) async { 23 + static Future<AppConfigEntity?> getAppConfig({bool forceSync = false}) async {
26 if (forceSync == false && configEntityEntity != null) { 24 if (forceSync == false && configEntityEntity != null) {
27 return configEntityEntity; 25 return configEntityEntity;
28 } 26 }
@@ -38,7 +36,8 @@ class AppConfigHelper { @@ -38,7 +36,8 @@ class AppConfigHelper {
38 // 是否需要隐藏... 36 // 是否需要隐藏...
39 static bool shouldHidePay() { 37 static bool shouldHidePay() {
40 return isIosPlatform() && 38 return isIosPlatform() &&
41 - (configEntityEntity?.isAppReviewing() == true || UserUtil.getUser()?.phoneNum == "17730280759"); 39 + (configEntityEntity?.isAppReviewing() == true ||
  40 + UserUtil.getUser()?.phoneNum == "17730280759");
42 } 41 }
43 42
44 // 获取app版本号 43 // 获取app版本号
@@ -51,22 +50,19 @@ class AppConfigHelper { @@ -51,22 +50,19 @@ class AppConfigHelper {
51 String versionCode = packageInfo.buildNumber; // 构建号 50 String versionCode = packageInfo.buildNumber; // 构建号
52 _versionCode = versionCode; 51 _versionCode = versionCode;
53 52
54 - debugPrint('versionName=$versionName versionCode=$versionCode platForm=${Platform.operatingSystem}'); 53 + debugPrint(
  54 + 'versionName=$versionName versionCode=$versionCode platForm=${Platform.operatingSystem}');
55 return versionCode; 55 return versionCode;
56 } 56 }
57 57
58 - static void saveAgreementAccepted(bool accepted) {  
59 - SpUtil.getInstance().setData(_prefsKeyAgreementAccepted, accepted); 58 + static Future<void> saveAgreementAccepted(bool accepted) async {
  59 + await SpUtil.getInstance().setData(_prefsKeyAgreementAccepted, accepted);
60 } 60 }
61 61
62 static bool getAgreementAccepted() { 62 static bool getAgreementAccepted() {
63 return SpUtil.getInstance().get<bool>(_prefsKeyAgreementAccepted) ?? false; 63 return SpUtil.getInstance().get<bool>(_prefsKeyAgreementAccepted) ?? false;
64 } 64 }
65 65
66 - static void _clearUserData() {  
67 - SpUtil.getInstance().remove(_prefsKeyAgreementAccepted);  
68 - }  
69 -  
70 static void exitApp() { 66 static void exitApp() {
71 if (Platform.isIOS) { 67 if (Platform.isIOS) {
72 exit(0); 68 exit(0);
lib/main.dart
@@ -8,17 +8,6 @@ import &#39;package:wow_english/app/app.dart&#39;; @@ -8,17 +8,6 @@ import &#39;package:wow_english/app/app.dart&#39;;
8 8
9 import 'common/request/basic_config.dart'; 9 import 'common/request/basic_config.dart';
10 10
11 -// void main() {  
12 -// ///设置设备默认方向  
13 -// WidgetsFlutterBinding.ensureInitialized();  
14 -// if (Platform.isAndroid) {  
15 -// SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( //设置状态栏透明  
16 -// statusBarColor: Colors.transparent,  
17 -// ));  
18 -// }  
19 -// runApp(const App());  
20 -// }  
21 -  
22 void main() { 11 void main() {
23 ///设置设备默认方向 12 ///设置设备默认方向
24 if (Platform.isAndroid) { 13 if (Platform.isAndroid) {
@@ -28,8 +17,8 @@ void main() { @@ -28,8 +17,8 @@ void main() {
28 } 17 }
29 18
30 final UmengApmSdk umengApmSdk = UmengApmSdk( 19 final UmengApmSdk umengApmSdk = UmengApmSdk(
31 - name: "",  
32 - bver: "", 20 + name: '',
  21 + bver: '',
33 22
34 // 是否开启SDK运行时日志输出 23 // 是否开启SDK运行时日志输出
35 enableLog: BasicConfig.isTestDev, 24 enableLog: BasicConfig.isTestDev,
@@ -56,12 +45,15 @@ void main() { @@ -56,12 +45,15 @@ void main() {
56 }, 45 },
57 46
58 ); 47 );
  48 +
  49 + // 此处只安装 Flutter 侧监控能力。Native APM/Common SDK 会保持未初始化状态,
  50 + // 直到用户同意隐私政策后才由 SplashPage 正式启动。
59 umengApmSdk.init(appRunner: (observer) async { 51 umengApmSdk.init(appRunner: (observer) async {
60 // 确保去掉原有的WidgetsFlutterBinding.ensureInitialized() ,以免出现重复初始化绑定的异常造成无法正常初始化,SDK内部已通过initFlutterBinding入参带入继承的WidgetsFlutterBinding实现初始化操作 52 // 确保去掉原有的WidgetsFlutterBinding.ensureInitialized() ,以免出现重复初始化绑定的异常造成无法正常初始化,SDK内部已通过initFlutterBinding入参带入继承的WidgetsFlutterBinding实现初始化操作
61 // 依赖ensureInitialized()初始化的代码可在此调用 53 // 依赖ensureInitialized()初始化的代码可在此调用
62 // 需要异步获取设置应用名称和版本号可在此回调中操作 54 // 需要异步获取设置应用名称和版本号可在此回调中操作
63 // SDK实例化的设置可先将name和bver 为 "",然后通过以下方式进行设置 55 // SDK实例化的设置可先将name和bver 为 "",然后通过以下方式进行设置
64 - PackageInfo packageInfo = await PackageInfo.fromPlatform(); 56 + final PackageInfo packageInfo = await PackageInfo.fromPlatform();
65 String packageName = packageInfo.packageName; 57 String packageName = packageInfo.packageName;
66 String buildNumber = packageInfo.buildNumber; 58 String buildNumber = packageInfo.buildNumber;
67 String version = packageInfo.version; 59 String version = packageInfo.version;
lib/utils/sp_util.dart
@@ -28,17 +28,17 @@ class SpUtil { @@ -28,17 +28,17 @@ class SpUtil {
28 return _instance!; 28 return _instance!;
29 } 29 }
30 30
31 - void setData<T>(String key, T data) { 31 + Future<void> setData<T>(String key, T data) async {
32 if (data is String) { 32 if (data is String) {
33 - _prefs?.setString(key, data); 33 + await _prefs?.setString(key, data);
34 } else if (data is double) { 34 } else if (data is double) {
35 - _prefs?.setDouble(key, data); 35 + await _prefs?.setDouble(key, data);
36 } else if (data is int) { 36 } else if (data is int) {
37 - _prefs?.setInt(key, data); 37 + await _prefs?.setInt(key, data);
38 } else if (data is bool) { 38 } else if (data is bool) {
39 - _prefs?.setBool(key, data); 39 + await _prefs?.setBool(key, data);
40 } else if (data is List<String>) { 40 } else if (data is List<String>) {
41 - _prefs?.setStringList(key, data); 41 + await _prefs?.setStringList(key, data);
42 } 42 }
43 } 43 }
44 44
pubspec.yaml
@@ -113,7 +113,7 @@ dependencies: @@ -113,7 +113,7 @@ dependencies:
113 # 应用内更新 https://pub-web.flutter-io.cn/packages/flutter_app_update 113 # 应用内更新 https://pub-web.flutter-io.cn/packages/flutter_app_update
114 flutter_app_update: ^3.0.4 114 flutter_app_update: ^3.0.4
115 # 友盟统计 https://pub-web.flutter-io.cn/packages/umeng_common_sdk 115 # 友盟统计 https://pub-web.flutter-io.cn/packages/umeng_common_sdk
116 - umeng_common_sdk: ^1.2.8 116 + umeng_common_sdk: ^1.3.1
117 # 友盟APM https://pub-web.flutter-io.cn/packages/umeng_apm_sdk 117 # 友盟APM https://pub-web.flutter-io.cn/packages/umeng_apm_sdk
118 umeng_apm_sdk: ^2.3.2 118 umeng_apm_sdk: ^2.3.2
119 # 嵌套滚动 https://pub.dev/packages/nested_scroll_views 119 # 嵌套滚动 https://pub.dev/packages/nested_scroll_views