204 lines
5.8 KiB
Dart
204 lines
5.8 KiB
Dart
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<HomeStartPage> createState() => _HomeStartPageState();
|
||
}
|
||
|
||
class _HomeStartPageState extends State<HomeStartPage> 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<void> _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<void> _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<void> _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);
|
||
}
|
||
} |