import 'dart:async'; import 'dart:io'; import 'package:aesthetica_wallpaper/core/app_ads_tools.dart'; import 'package:flutter/material.dart'; import 'package:app_tracking_transparency/app_tracking_transparency.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import '../widgets/main_screen.dart'; class HomeStartPage extends StatefulWidget { HomeStartPage({Key? key}) : super(key: key); @override State createState() => _HomeStartPageState(); } class _HomeStartPageState extends State with WidgetsBindingObserver { Timer? _timeoutTimer; bool _isNavigating = false; // 使用这个标志位来防止并发请求,而不是彻底锁死 bool _isRequestingATT = false; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addPostFrameCallback((_) { _initPermissionsAndSDK(); }); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); _timeoutTimer?.cancel(); super.dispose(); } /// 监听生命周期:这是通过审核的关键“补救”措施 @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { // 审核场景:审核员启动App -> 立即切后台 -> 再切回来 // 此时如果 ATT 还没弹出来,我们需要检查并再次触发 _checkATTOnResume(); } } /// 回到前台时的检查逻辑 Future _checkATTOnResume() async { if (Platform.isIOS) { try { // 只有当状态依然是“未决定”时,才去尝试请求 // 如果用户已经点过允许或拒绝,这里就不会再打扰用户 final status = await AppTrackingTransparency.trackingAuthorizationStatus; if (status == TrackingStatus.notDetermined) { debugPrint("App resumed and ATT is not determined. Retrying request..."); await _requestATT(); } } catch (e) { debugPrint("Error checking ATT on resume: $e"); } } } Future _initPermissionsAndSDK() async { // 1. 启动时的安全延迟,等待 LaunchScreen 消失 await Future.delayed(const Duration(milliseconds: 500)); if (!mounted) return; // 2. iOS ATT 授权 if (Platform.isIOS) { await _requestATT(); } // 3. 初始化 SDK try { await AppAdsTools.instance.init(); debugPrint("✅ Max SDK Initialized"); } catch (error) { debugPrint("❌ SDK Initialization failed: $error"); } // 4. 加载广告 if (mounted) { _startAdLoadingProcess(); } } /// 核心 ATT 请求逻辑(加入了防并发锁) Future _requestATT() async { // 如果当前正在请求中,直接返回,防止重复弹窗 if (_isRequestingATT) return; _isRequestingATT = true; try { TrackingStatus status = await AppTrackingTransparency.trackingAuthorizationStatus; // 只有未决定的状态才弹窗 if (status == TrackingStatus.notDetermined) { debugPrint("Requesting ATT Authorization..."); // 请求弹窗 status = await AppTrackingTransparency.requestTrackingAuthorization(); debugPrint("ATT Status Result: $status"); // 只有当用户真正做出了选择(不再是 notDetermined),才延迟一下给动画时间 if (status != TrackingStatus.notDetermined) { await Future.delayed(const Duration(milliseconds: 500)); } } } catch (e) { debugPrint("ATT Request Error: $e"); } finally { // 无论成功失败,释放锁,允许后续重试(万一失败了) _isRequestingATT = false; } } void _startAdLoadingProcess() { debugPrint("开始加载开屏广告..."); // 如果之前已经有个定时器(比如因为生命周期变化重复触发),先取消掉 _timeoutTimer?.cancel(); _timeoutTimer = Timer(const Duration(seconds: 15), () { debugPrint("广告加载超时 (15秒)。"); _navigateToHome(); }); AppAdsTools.instance.loadInitialSplashAd( onAdReady: (AdPlacement placement) { debugPrint("开屏广告已就绪: ${placement.name}"); _navigateToHome(adHomePlacement: placement); }, onAllAdsFailed: () { debugPrint("所有开屏广告都加载失败。"); _navigateToHome(); }, ); } void _navigateToHome({AdPlacement? adHomePlacement}) { if (_isNavigating) return; _isNavigating = true; _timeoutTimer?.cancel(); if (!mounted) return; Navigator.of(context).pushReplacement( PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => MainScreen(adShowPlacement: adHomePlacement), ), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color(0xff1F2124), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( 'assets/images/aesthloco.png', width: 100, height: 100, ), const SizedBox(height: 5), const Text( '.Resource Loading.', style: TextStyle( fontSize: 14, color: Color(0xff7D45E5), fontWeight: FontWeight.w500, decoration: TextDecoration.none, ), ), const SizedBox(height: 5), const SpinKitCubeGridIndicator(), ], ), ), ); } } class SpinKitCubeGridIndicator extends StatelessWidget { const SpinKitCubeGridIndicator({super.key}); @override Widget build(BuildContext context) { return const SpinKitThreeBounce(color: Color(0xff7D45E5), size: 23.0); } }