import 'dart:async'; import 'dart:io'; import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:limiting_direction_csx/limiting_direction_csx.dart'; import 'package:umeng_common_sdk/umeng_common_sdk.dart'; import 'package:wow_english/common/core/app_config_helper.dart'; import 'package:wow_english/common/core/user_util.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/common/request/config.dart'; import 'package:wow_english/common/request/dao/user_dao.dart'; import 'package:wow_english/models/user_entity.dart'; import 'package:wow_english/route/route.dart'; import 'package:wow_english/utils/audio_player_util.dart'; import 'package:wow_english/utils/log_util.dart'; import 'package:wow_english/utils/sp_util.dart'; import '../common/core/app_consts.dart'; import '../common/core/module_cache.dart'; import '../common/widgets/webview_dialog.dart'; import '../common/utils/staticstring/userregister.dart'; class SplashPage extends StatelessWidget { const SplashPage({super.key}); @override Widget build(BuildContext context) { return const TransitionView(); } } class TransitionView extends StatefulWidget { const TransitionView({super.key}); @override State createState() { return _TransitionViewState(); } } class _TransitionViewState extends State { bool _isPrivacyDecisionInProgress = false; bool _isInitializing = false; Future startTime() async { // 判断是否登录 UserEntity? userEntity = UserUtil.getUser(); Log.d( '当前模式:kDebugMode=$kDebugMode, kProfileMode=$kProfileMode, kReleaseMode=$kReleaseMode'); Log.d('当前API地址:${RequestConfig.baseUrl}'); //BuildContext context = Navigator.of(context).context; //var currentPageName = ModalRoute.of(Navigator.of(AppRouter.context))?.settings.name; //Log.d('Splash读当前页面:$currentPageName'); Log.d('Splash读本地, userEntity: $userEntity'); int apartInMilliseconds = 0; // 获取系统配置信息 AppConfigHelper.getAppConfig(); // 调一下接口判断一下有效性再往下 if (userEntity != null && userEntity.token != null) { String token = userEntity.token!; DateTime startTime = DateTime.now(); await fetchNecessaryData(token); apartInMilliseconds = DateTime.now().difference(startTime).inMilliseconds; } int duration = max(1500 - apartInMilliseconds, 0); Log.d( 'Splash getUserInfo 耗时:${apartInMilliseconds}ms, 最终duration=$duration'); Timer(Duration(milliseconds: duration), () { pushNamedAndRemoveUntil(AppRouteName.home, (route) => false); }); } void _showPrivacyDialog() { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return PopScope( canPop: false, onPopInvoked: (didPop) { Log.d('WQF privacy dialog onPopInvoked didPop=$didPop'); }, child: child(), ); }, ); } Widget child() { if (Platform.isIOS) { return AlertDialog( title: const Center( child: Text("服务条款及隐私政策"), ), content: SizedBox( width: MediaQuery.of(context).size.height, height: MediaQuery.of(context).size.height, child: const SingleChildScrollView( child: Text(UserRegisterString.userPrivacyPolicyStr), ), ), actions: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ TextButton( onPressed: _acceptPrivacyAgreement, child: const Text("同意并继续", style: TextStyle(color: Color(0xFFFBB621))), ), TextButton( onPressed: _rejectPrivacyAgreement, child: const Text("不同意,退出应用"), ), ], ) ], ); } return WebviewDialog( title: "服务条款及隐私政策", webUrl: AppConsts.userPrivacyPolicyUrl, leftButtonText: '同意并继续', rightButtonText: '不同意,退出应用', leftTap: _acceptPrivacyAgreement, rightTap: _rejectPrivacyAgreement, ); } Future _acceptPrivacyAgreement() async { if (_isPrivacyDecisionInProgress) return; _isPrivacyDecisionInProgress = true; await AppConfigHelper.saveAgreementAccepted(true); if (!mounted) return; // 先关闭隐私政策弹窗,再进行可能耗时的 SDK 和业务初始化。 Navigator.of(context, rootNavigator: true).pop(); await _startInitialization(); _isPrivacyDecisionInProgress = false; } Future _rejectPrivacyAgreement() async { if (_isPrivacyDecisionInProgress) return; _isPrivacyDecisionInProgress = true; // 显式撤销本地授权并等待写入完成,避免退出后残留 true。 await AppConfigHelper.saveAgreementAccepted(false); AppConfigHelper.exitApp(); } Future _startInitialization() async { if (_isInitializing) return; _isInitializing = true; try { await _initData(); } catch (error, stackTrace) { Log.d('Splash初始化失败: $error\n$stackTrace'); _isInitializing = false; if (mounted) { _showInitializationErrorDialog(); } } } void _showInitializationErrorDialog() { showDialog( context: context, barrierDismissible: false, builder: (dialogContext) => AlertDialog( title: const Text('温馨提示'), content: const Text('初始化失败,请检查网络后重试'), actions: [ const TextButton( onPressed: AppConfigHelper.exitApp, child: Text('退出'), ), TextButton( onPressed: () { Navigator.of(dialogContext).pop(); _startInitialization(); }, child: const Text('重试'), ), ], ), ); } ///初始化数据 Future _initData() async { // 用户同意后,先准备 Native APM,再正式初始化 Common SDK。 if (Platform.isAndroid) { const MethodChannel umengMethodChannel = MethodChannel('wow_english/umeng_method_channel'); await umengMethodChannel.invokeMethod('prepareAfterPrivacyConsent'); } // Android 插件执行 initCommon 后不会回调 MethodChannel result,返回的 // Future 永远不结束,因此这里只触发初始化,不能 await。 unawaited(UmengCommonSdk.initCommon( "663b66b0b3362515012f4ea5", "663b66ecf32cc41105ae74b7", "official") .catchError((error, stackTrace) { Log.d('友盟Common SDK初始化失败: $error\n$stackTrace'); })); await AudioPlayerUtil.getInstance() .playAudio(AudioPlayerUtilType.welcomeToWow) .timeout(const Duration(seconds: 10), onTimeout: () { Log.d('欢迎音频播放超时,继续进入首页'); }); await startTime(); } Future fetchNecessaryData(String userToken, {Completer? completer}) async { completer ??= Completer(); // 获取用户信息 try { UserEntity? userEntity = await UserDao.getUserInfo(); Log.d('Splash在线更新, userEntity: $userEntity'); if (userEntity != null) { userEntity.token = userToken; Log.d('Splash重设token, userEntity: $userEntity'); UserUtil.saveUser(userEntity); } //todo 如果为null是否需要清除用户信息? completer.complete(); } catch (e) { debugPrint('Splash获取用户信息异常:$e'); if (!mounted) { if (!completer.isCompleted) { completer.complete(); } return; } //异常的话弹窗提示重试 showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return PopScope( canPop: false, onPopInvoked: (didPop) { Log.d('WQF fetchNecessaryData onPopInvoked didPop=$didPop'); }, child: AlertDialog( title: const Text('温馨提示'), content: const Text('网络异常,请检查网络后重试'), actions: [ TextButton( child: const Text('退出'), onPressed: () { // 关闭对话框并退出应用 Navigator.of(context).pop(); AppConfigHelper.exitApp(); // 只有在 completer 还没有完成时才完成它 if (completer != null && !completer.isCompleted) { completer.complete(); } }), TextButton( child: const Text('重试'), onPressed: () async { // 关闭对话框并重试 Navigator.of(context).pop(); await fetchNecessaryData(userToken, completer: completer); // 不需要再次调用 completer.complete(),因为 fetchNecessaryData 内部已经处理了 }, ), ], ), ); }, ); e.logE(); } // 等待completer完成,这会阻塞后续代码的执行,直到用户做出选择 await completer.future; } @override void initState() { super.initState(); init(); } void init() async { changeDevice(); await SpUtil.preInit(); ModuleCache.instance.init(); if (AppConfigHelper.getAgreementAccepted()) { await _startInitialization(); } else { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { _showPrivacyDialog(); } }); } } void changeDevice() async { ///设置设备默认方向 WidgetsFlutterBinding.ensureInitialized(); if (Platform.isIOS) { await LimitingDirectionCsx.setScreenDirection( DeviceDirectionMask.Landscape); } else { await SystemChrome.setPreferredOrientations( [DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]); } await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Container( decoration: BoxDecoration( image: DecorationImage( image: AssetImage( 'splash'.assetGif, ), fit: BoxFit.fill)), ), ), ); } }