283 lines
8.4 KiB
Dart
283 lines
8.4 KiB
Dart
import 'dart:io' show Platform;
|
||
import 'package:flutter/material.dart';
|
||
import 'package:unity_levelplay_mediation/unity_levelplay_mediation.dart';
|
||
|
||
import '../screens/main_screen.dart';
|
||
import 'app_ads_managers.dart';
|
||
|
||
|
||
/// Loading 页面 - 显示 logo 和 loading 动画,初始化广告
|
||
class StartPage extends StatefulWidget {
|
||
final Widget Function()? homePageBuilder;
|
||
|
||
const StartPage({super.key, this.homePageBuilder});
|
||
|
||
@override
|
||
State<StartPage> createState() => _StartPageState();
|
||
}
|
||
|
||
class _StartPageState extends State<StartPage> with WidgetsBindingObserver {
|
||
final InterstitialAdManager _adManager = InterstitialAdManager();
|
||
bool _hasRequestedATT = false;
|
||
bool _isInitializing = false;
|
||
bool _isWaitingForATT = false;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
WidgetsBinding.instance.addObserver(this);
|
||
_initializeAds();
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
WidgetsBinding.instance.removeObserver(this);
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||
super.didChangeAppLifecycleState(state);
|
||
|
||
// 当应用从后台回到前台时,如果还没有请求过ATT,则请求
|
||
if (state == AppLifecycleState.resumed &&
|
||
!_hasRequestedATT &&
|
||
Platform.isIOS &&
|
||
!_isInitializing) {
|
||
_requestATTWhenReady();
|
||
}
|
||
}
|
||
|
||
Future<void> _initializeAds() async {
|
||
_isInitializing = true;
|
||
|
||
try {
|
||
// iOS 平台:智能等待并请求 ATT 权限
|
||
if (Platform.isIOS) {
|
||
await _requestATTWhenReady();
|
||
}
|
||
|
||
// ATT 请求完成后,初始化广告管理器
|
||
_adManager.initialize();
|
||
|
||
// 等待初始化完成
|
||
await _waitForInitialization();
|
||
|
||
if (mounted) {
|
||
// 并行加载三个广告,任意一个成功就立即跳转并显示
|
||
_adManager.loadInitialSplashAd(
|
||
onAdReady: (adType) {
|
||
if (mounted) {
|
||
// 延迟一小段时间后跳转并显示广告
|
||
Future.delayed(const Duration(milliseconds: 500)).then((_) {
|
||
if (mounted) {
|
||
_navigateToHomeAndShowAd(adType);
|
||
}
|
||
});
|
||
}
|
||
},
|
||
onAllAdsFailed: () {
|
||
if (mounted) {
|
||
// 所有广告都失败,延迟后进入主页
|
||
Future.delayed(const Duration(seconds: 1)).then((_) {
|
||
if (mounted) {
|
||
_navigateToHome();
|
||
}
|
||
});
|
||
}
|
||
},
|
||
);
|
||
}
|
||
} catch (e) {
|
||
if (mounted) {
|
||
// 即使初始化失败,也允许进入主页(广告可能稍后可用)
|
||
await Future.delayed(const Duration(seconds: 2));
|
||
if (mounted) {
|
||
_navigateToHome();
|
||
}
|
||
}
|
||
} finally {
|
||
_isInitializing = false;
|
||
}
|
||
}
|
||
|
||
/// 导航到主页并显示广告
|
||
void _navigateToHomeAndShowAd(InterstitialAdType adType) {
|
||
// 对于开屏场景,直接跳到 HomePage,并把已准备好的广告类型传过去
|
||
Navigator.of(context).pushReplacement(
|
||
MaterialPageRoute(
|
||
builder: (context) => MainScreen(initialSplashAdType: adType),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 导航到主页
|
||
void _navigateToHome() {
|
||
if (widget.homePageBuilder != null) {
|
||
Navigator.of(context).pushReplacement(
|
||
MaterialPageRoute(builder: (context) => widget.homePageBuilder!()),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 智能等待并请求 ATT 权限,确保没有其他权限对话框在显示
|
||
Future<void> _requestATTWhenReady() async {
|
||
if (_hasRequestedATT || _isWaitingForATT) return;
|
||
|
||
_isWaitingForATT = true;
|
||
|
||
try {
|
||
// 检查当前ATT状态
|
||
final currentStatus =
|
||
await ATTrackingManager.getTrackingAuthorizationStatus();
|
||
debugPrint('LoadingPage: ATT Status: $currentStatus');
|
||
|
||
if (currentStatus != ATTStatus.NotDetermined) {
|
||
_hasRequestedATT = true;
|
||
_isWaitingForATT = false;
|
||
return;
|
||
}
|
||
|
||
// 初始延迟,让系统处理其他权限
|
||
await Future.delayed(const Duration(milliseconds: 2000));
|
||
|
||
// 智能等待:检测是否有其他系统对话框
|
||
await _waitForSystemDialogsToClear();
|
||
|
||
// 最终检查:确保状态仍然是 NotDetermined
|
||
final finalStatus =
|
||
await ATTrackingManager.getTrackingAuthorizationStatus();
|
||
if (finalStatus == ATTStatus.NotDetermined && mounted) {
|
||
_hasRequestedATT = true;
|
||
debugPrint('LoadingPage: Requesting ATT permission...');
|
||
final returnedStatus =
|
||
await ATTrackingManager.requestTrackingAuthorization();
|
||
debugPrint('LoadingPage: ATT Status returned: $returnedStatus');
|
||
} else {
|
||
_hasRequestedATT = true;
|
||
}
|
||
} catch (e) {
|
||
debugPrint('LoadingPage: ATT request error: $e');
|
||
_hasRequestedATT = true;
|
||
} finally {
|
||
_isWaitingForATT = false;
|
||
}
|
||
}
|
||
|
||
/// 等待系统对话框清除
|
||
/// 通过监听应用生命周期状态变化来检测是否有系统对话框
|
||
Future<void> _waitForSystemDialogsToClear() async {
|
||
const maxWaitTime = Duration(seconds: 30); // 最多等待30秒
|
||
const checkInterval = Duration(milliseconds: 500);
|
||
final startTime = DateTime.now();
|
||
|
||
AppLifecycleState? lastState = WidgetsBinding.instance.lifecycleState;
|
||
int stableCount = 0;
|
||
const requiredStableCount = 4; // 需要连续2秒保持稳定状态
|
||
|
||
while (DateTime.now().difference(startTime) < maxWaitTime) {
|
||
if (!mounted) break;
|
||
|
||
await Future.delayed(checkInterval);
|
||
|
||
final currentState = WidgetsBinding.instance.lifecycleState;
|
||
|
||
// 如果应用状态稳定在 resumed,说明没有系统对话框
|
||
if (currentState == AppLifecycleState.resumed) {
|
||
if (lastState == AppLifecycleState.resumed) {
|
||
stableCount++;
|
||
if (stableCount >= requiredStableCount) {
|
||
debugPrint('LoadingPage: App state stable, ready for ATT request');
|
||
break;
|
||
}
|
||
} else {
|
||
stableCount = 1;
|
||
}
|
||
} else {
|
||
// 如果状态不是 resumed,重置计数
|
||
stableCount = 0;
|
||
debugPrint('LoadingPage: App state: $currentState, waiting...');
|
||
}
|
||
|
||
lastState = currentState;
|
||
}
|
||
|
||
// 额外的安全延迟
|
||
await Future.delayed(const Duration(milliseconds: 1000));
|
||
}
|
||
|
||
/// 等待广告管理器初始化完成
|
||
Future<void> _waitForInitialization() async {
|
||
// 轮询检查初始化状态,最多等待 15 秒
|
||
const maxWaitTime = Duration(seconds: 15);
|
||
const checkInterval = Duration(milliseconds: 200);
|
||
final startTime = DateTime.now();
|
||
|
||
while (DateTime.now().difference(startTime) < maxWaitTime) {
|
||
if (_adManager.isInitialized) {
|
||
return;
|
||
}
|
||
await Future.delayed(checkInterval);
|
||
}
|
||
|
||
// 如果超时仍未初始化,记录警告但继续
|
||
if (!_adManager.isInitialized) {
|
||
debugPrint('Warning: Ad initialization timeout, proceeding anyway');
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
backgroundColor: Color(0xff141420),
|
||
body: Center(
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
// Logo with animation
|
||
TweenAnimationBuilder<double>(
|
||
tween: Tween(begin: 0.0, end: 1.0),
|
||
duration: const Duration(milliseconds: 800),
|
||
curve: Curves.easeOut,
|
||
builder: (context, value, child) {
|
||
return Opacity(
|
||
opacity: value,
|
||
child: Transform.scale(scale: value, child: child),
|
||
);
|
||
},
|
||
child: Image.asset(
|
||
'assets/images/stonicons.png',
|
||
width: 100,
|
||
height: 100,
|
||
fit: BoxFit.contain,
|
||
),
|
||
),
|
||
const SizedBox(height: 5),
|
||
// 状态文本
|
||
const Text(
|
||
'Resource Loading',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
color: Colors.white,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
const SizedBox(height: 5),
|
||
// Loading 动画
|
||
const SizedBox(
|
||
width: 30,
|
||
height: 30,
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 4,
|
||
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFFFFFFFF)),
|
||
),
|
||
),
|
||
const SizedBox(height: 30),
|
||
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|