247 lines
7.8 KiB
Dart
247 lines
7.8 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'dart:async';
|
||
import 'package:provider/provider.dart';
|
||
import 'providers/expense_provider.dart';
|
||
import 'screens/home_screen.dart';
|
||
import 'package:flutter_unionad/flutter_unionad.dart';
|
||
void main() {
|
||
WidgetsFlutterBinding.ensureInitialized();
|
||
runApp(const MyApp());
|
||
}
|
||
|
||
class MyApp extends StatelessWidget {
|
||
const MyApp({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return MultiProvider(
|
||
providers: [
|
||
ChangeNotifierProvider(create: (_) => ExpenseProvider()..loadData()),
|
||
],
|
||
child: MaterialApp(
|
||
title: 'Expense Tracker',
|
||
theme: ThemeData(
|
||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||
useMaterial3: true,
|
||
),
|
||
home: const SplashPage(),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
class SplashPage extends StatefulWidget {
|
||
const SplashPage({super.key});
|
||
|
||
@override
|
||
State<SplashPage> createState() => _SplashPageState();
|
||
}
|
||
|
||
class _SplashPageState extends State<SplashPage> {
|
||
bool _showAd = true;
|
||
Timer? _adWatchdog;
|
||
bool? _initSuccess; // null=loading, true=success, false=failure
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
// 延后到首帧绘制后再初始化广告 SDK,避免在 Activity/Context 未就绪时调用导致 NPE
|
||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
_initUnionAd();
|
||
});
|
||
// 如果广告或 SDK 未在合理时间内返回,使用回退跳转避免白屏
|
||
_adWatchdog = Timer(const Duration(seconds: 6), () {
|
||
if (mounted) _goToHome();
|
||
});
|
||
}
|
||
|
||
Future<void> _initUnionAd() async {
|
||
// 注册并初始化 FlutterUnionad SDK
|
||
try {
|
||
await FlutterUnionad.register(
|
||
// 穿山甲广告 Android appid 必填
|
||
androidAppId: "5779303",
|
||
// 穿山甲广告 ios appid 必填
|
||
iosAppId: "5779303",
|
||
// appname 必填
|
||
appName: "晴天记账",
|
||
// 使用聚合功能(true 使用 GroMore 下的广告位)
|
||
useMediation: false,
|
||
// 是否为计费用户
|
||
paid: false,
|
||
// 用户画像关键词
|
||
keywords: "",
|
||
// 是否允许 SDK 展示通知栏提示
|
||
allowShowNotify: true,
|
||
// 是否显示 debug 日志
|
||
debug: true,
|
||
// 是否支持多进程
|
||
supportMultiProcess: false,
|
||
// 允许直接下载的网络状态集合
|
||
directDownloadNetworkType: [
|
||
FlutterUnionadNetCode.NETWORK_STATE_2G,
|
||
FlutterUnionadNetCode.NETWORK_STATE_3G,
|
||
FlutterUnionadNetCode.NETWORK_STATE_4G,
|
||
FlutterUnionadNetCode.NETWORK_STATE_WIFI,
|
||
],
|
||
androidPrivacy: AndroidPrivacy(
|
||
isCanUseLocation: false,
|
||
lat: 0.0,
|
||
lon: 0.0,
|
||
isCanUsePhoneState: false,
|
||
imei: "",
|
||
isCanUseWifiState: false,
|
||
macAddress: "",
|
||
isCanUseWriteExternal: false,
|
||
oaid: "b69cd3cf68900323",
|
||
alist: false,
|
||
isCanUseAndroidId: false,
|
||
androidId: "",
|
||
isCanUsePermissionRecordAudio: false,
|
||
isLimitPersonalAds: false,
|
||
isProgrammaticRecommend: false,
|
||
userPrivacyConfig: {"mcod": "0"},
|
||
),
|
||
iosPrivacy: IOSPrivacy(
|
||
limitPersonalAds: false,
|
||
limitProgrammaticAds: false,
|
||
forbiddenCAID: false,
|
||
),
|
||
// userInfo: UnionadUserInfo(
|
||
// userId: "unionad_123",
|
||
// age: 19,
|
||
// gender: 2,
|
||
// channel: "flutter",
|
||
// subChannel: "flutter_unionad",
|
||
// userValueGroup: "QQ",
|
||
// customInfos: {"QQ": "123", "WeChat": "456"},
|
||
// ),
|
||
// localConfig: "site_config_5098580",
|
||
);
|
||
debugPrint('FlutterUnionad.register: success');
|
||
if (mounted) setState(() => _initSuccess = true);
|
||
} catch (e) {
|
||
debugPrint('FlutterUnionad.register error: $e');
|
||
if (mounted) setState(() => _initSuccess = false);
|
||
}
|
||
try {
|
||
final sdkVersion = await FlutterUnionad.getSDKVersion();
|
||
debugPrint('FlutterUnionad SDK Version: $sdkVersion');
|
||
} catch (e) {
|
||
debugPrint('getSDKVersion error: $e');
|
||
}
|
||
|
||
FlutterUnionad.requestPermissionIfNecessary(
|
||
callBack: FlutterUnionadPermissionCallBack(
|
||
notDetermined: () {
|
||
debugPrint('权限未确定');
|
||
},
|
||
restricted: () {
|
||
debugPrint('权限限制');
|
||
},
|
||
denied: () {
|
||
debugPrint('权限拒绝');
|
||
},
|
||
authorized: () {
|
||
debugPrint('权限同意');
|
||
},
|
||
),
|
||
);
|
||
// 如果注册/初始化在合理时间内仍未完成,标记为失败以便调试
|
||
Future.delayed(const Duration(seconds: 4), () {
|
||
if (!mounted) return;
|
||
if (_initSuccess == null) {
|
||
debugPrint('FlutterUnionad: init did not finish within 4s, marking as failed');
|
||
setState(() => _initSuccess = false);
|
||
}
|
||
});
|
||
}
|
||
|
||
void _goToHome() {
|
||
if (!mounted) return;
|
||
_adWatchdog?.cancel();
|
||
Navigator.of(context).pushReplacement(
|
||
MaterialPageRoute(builder: (_) => const HomeScreen()),
|
||
);
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_adWatchdog?.cancel();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
body: Stack(
|
||
children: [
|
||
// 后端主页面作为备用
|
||
const Offstage(offstage: true, child: HomeScreen()),
|
||
// 开屏广告视图
|
||
Positioned.fill(
|
||
child: _showAd
|
||
? FlutterUnionadSplashAdView(
|
||
androidCodeId: "102729400",
|
||
iosCodeId: "102729400",
|
||
supportDeepLink: true,
|
||
width: MediaQuery.of(context).size.width,
|
||
height: MediaQuery.of(context).size.height,
|
||
hideSkip: false,
|
||
timeout: 3000,
|
||
callBack: FlutterUnionadSplashCallBack(
|
||
onShow: () {
|
||
debugPrint('开屏广告显示');
|
||
setState(() => _showAd = true);
|
||
},
|
||
onClick: () {
|
||
debugPrint('开屏广告点击');
|
||
},
|
||
onFail: (error) {
|
||
debugPrint('开屏广告失败 $error');
|
||
_goToHome();
|
||
},
|
||
onFinish: () {
|
||
debugPrint('开屏广告倒计时结束');
|
||
_goToHome();
|
||
},
|
||
onSkip: () {
|
||
debugPrint('开屏广告跳过');
|
||
_goToHome();
|
||
},
|
||
onTimeOut: () {
|
||
debugPrint('开屏广告超时');
|
||
_goToHome();
|
||
},
|
||
),
|
||
)
|
||
: const SizedBox.shrink(),
|
||
),
|
||
// Debug: init 状态可视化,方便在真机上观察
|
||
Positioned(
|
||
left: 12,
|
||
right: 12,
|
||
bottom: 24,
|
||
child: Align(
|
||
alignment: Alignment.bottomCenter,
|
||
child: Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||
decoration: BoxDecoration(
|
||
color: Colors.black.withOpacity(0.6),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: Text(
|
||
_initSuccess == null
|
||
? 'SDK init: loading...'
|
||
: (_initSuccess == true ? 'SDK init: success' : 'SDK init: failed'),
|
||
style: const TextStyle(color: Colors.white),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|