初始化
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/assets_tool/app_colors.dart';
|
||||
import 'package:hgdj/assets_tool/images.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
|
||||
import '../../hj_model/splash/ads_model.dart';
|
||||
import '../../routers/jump_router.dart';
|
||||
import '../../tools_base/banner/ads_banner_widget.dart';
|
||||
import '../../tools_base/banner/ads_item.dart';
|
||||
import '../../tools_base/widget/net_image_widget.dart';
|
||||
|
||||
/// 启动页广告:3 秒倒计时后右上角按钮变为"关闭",点击触发 callback 跳首页
|
||||
class SplashAdView extends StatefulWidget {
|
||||
final List<AdsInfoModel>? adData;
|
||||
final Function? callback;
|
||||
|
||||
const SplashAdView({
|
||||
super.key,
|
||||
this.adData,
|
||||
this.callback,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _SplashAdViewState();
|
||||
}
|
||||
|
||||
class _SplashAdViewState extends State<SplashAdView> {
|
||||
int count = 3;
|
||||
Timer? timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
timer = Timer.periodic(const Duration(seconds: 1), (t) {
|
||||
count--;
|
||||
setState(() {});
|
||||
if (count <= 0) t.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
timer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
children: <Widget>[
|
||||
_buildAdContent(),
|
||||
SafeArea(
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 28, right: 17),
|
||||
child: _buildCountdownButton(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 三种展示形态:1 张走单图点击跳转,多张走轮播组件,无广告则显示默认启动图
|
||||
Widget _buildAdContent() {
|
||||
final dataLen = widget.adData?.length ?? 0;
|
||||
if (dataLen == 1) {
|
||||
final ad = widget.adData!.first;
|
||||
return GestureDetector(
|
||||
onTap: () => pushToPageByLink(ad.href ?? ""),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
child: NetworkImageLoader(
|
||||
imageUrl: ad.cover ?? "",
|
||||
width: screen.screenWidth,
|
||||
height: screen.screenHeight,
|
||||
fit: BoxFit.cover,
|
||||
placeHolderWidget: _splashBg(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (dataLen > 1) {
|
||||
return Container(
|
||||
color: Color(0xff151515),
|
||||
child: SplashBannerWidget(adData: widget.adData ?? []),
|
||||
);
|
||||
}
|
||||
return _splashBg();
|
||||
}
|
||||
|
||||
Widget _splashBg() => Image.asset(
|
||||
'ic_splash_bg.webp'.launchPath,
|
||||
fit: BoxFit.fill,
|
||||
width: Get.width,
|
||||
height: Get.height,
|
||||
);
|
||||
|
||||
Widget _buildCountdownButton() {
|
||||
return GestureDetector(
|
||||
// 倒计时未结束时点击无效,避免用户误触跳过广告
|
||||
onTap: () {
|
||||
if (count <= 0) widget.callback?.call();
|
||||
},
|
||||
child: Container(
|
||||
height: 36,
|
||||
width: 77,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: 0.5),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
transitionBuilder: (child, anim) {
|
||||
// 倒计时递减:新数字从上滑入、旧数字向下滑出(下翻效果)
|
||||
final isIncoming = child.key == ValueKey(count);
|
||||
final begin = isIncoming ? const Offset(0, -1) : const Offset(0, 1);
|
||||
return ClipRect(
|
||||
child: SlideTransition(
|
||||
position:
|
||||
Tween<Offset>(begin: begin, end: Offset.zero).animate(anim),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: count > 0
|
||||
? Text(
|
||||
count.toString(),
|
||||
key: ValueKey(count),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
)
|
||||
: const Text(
|
||||
"关闭",
|
||||
key: ValueKey(0),
|
||||
style: TextStyle(color: Colors.white, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动页多广告轮播:2.5 秒自动切换,底部圆点指示器,左右双向无限滑
|
||||
class SplashBannerWidget extends StatefulWidget {
|
||||
final List<AdsInfoModel> adData;
|
||||
|
||||
const SplashBannerWidget({
|
||||
super.key,
|
||||
required this.adData,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _SplashBannerWidgetState();
|
||||
}
|
||||
|
||||
class _SplashBannerWidgetState extends State<SplashBannerWidget> {
|
||||
late final PageController _pageCtr;
|
||||
Timer? _timer;
|
||||
|
||||
List<AdsInfoModel> get _ads => widget.adData;
|
||||
int get _len => _ads.length;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pageCtr = PageController(initialPage: 0);
|
||||
_pageCtr.addListener(_onPageScroll);
|
||||
// 延迟启动自动轮播,避免和入场动画冲突;mounted 检查防止 dispose 后启动 timer
|
||||
if (_len > 1) {
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
if (!mounted) return;
|
||||
_timer = Timer.periodic(const Duration(milliseconds: 2500), _onTimer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer?.cancel();
|
||||
_pageCtr.removeListener(_onPageScroll);
|
||||
_pageCtr.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onPageScroll() {
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
void _onTimer(Timer timer) {
|
||||
final page = _pageCtr.page?.toInt() ?? 0;
|
||||
_pageCtr.animateToPage(
|
||||
page + 1,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_ads.isEmpty) return const SizedBox();
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
PageView.builder(
|
||||
controller: _pageCtr,
|
||||
itemCount: 1000 + _len * 100,
|
||||
allowImplicitScrolling: true,
|
||||
onPageChanged: (index) => setState(() {}),
|
||||
itemBuilder: (ctx, index) {
|
||||
final ad = _ads[index % _len];
|
||||
return AdsItem(
|
||||
adInfo: ad,
|
||||
showType: AdShowType.splash,
|
||||
);
|
||||
},
|
||||
),
|
||||
Positioned(
|
||||
bottom: 7,
|
||||
child: _buildIndicator(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIndicator() {
|
||||
int curPageIndex = 0;
|
||||
if (_pageCtr.hasClients && _ads.isNotEmpty) {
|
||||
curPageIndex = ((_pageCtr.offset + 200.0) ~/ screen.screenWidth) % _len;
|
||||
}
|
||||
// 复用共用指示器(自带切换动画);尺寸沿用启动页原值
|
||||
return SizedBox(
|
||||
width: Get.width - 14 * 2,
|
||||
child: Center(
|
||||
child: CIndicator(
|
||||
itemCount: _len,
|
||||
selectIndex: curPageIndex,
|
||||
space: 10,
|
||||
dotSize: 8,
|
||||
selectWidth: 25,
|
||||
color: Colors.white.withValues(alpha: 0.5),
|
||||
selectColor: AppColors.actionRed.withValues(alpha: 0.5),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/config/address.dart';
|
||||
import 'package:hgdj/config/config.dart';
|
||||
import 'package:hgdj/hj_model/home/plate_model.dart';
|
||||
import 'package:hgdj/hj_model/splash/ads_model.dart';
|
||||
import 'package:hgdj/hj_model/splash/cut_info.dart';
|
||||
import 'package:hgdj/hj_model/splash/domain_source_model.dart';
|
||||
import 'package:hgdj/hj_model/user/user_info_model.dart';
|
||||
import 'package:hgdj/hj_page/main_page/main_logic.dart';
|
||||
import 'package:hgdj/hj_page/main_page/main_page.dart';
|
||||
import 'package:hgdj/hj_page/pre_sale/limit_time_provider.dart';
|
||||
import 'package:hgdj/hj_page/pre_sale/pre_sale_provider.dart';
|
||||
import 'package:hgdj/hj_utils/api_service/common_service.dart';
|
||||
import 'package:hgdj/hj_utils/detect_line_manager.dart';
|
||||
import 'package:hgdj/hj_utils/local_server.dart';
|
||||
import 'package:hgdj/hj_utils/local_server_guard.dart';
|
||||
import 'package:hgdj/hj_utils/text_util.dart';
|
||||
import 'package:hgdj/hj_utils/version_util.dart';
|
||||
import 'package:hgdj/tools_base/ad_manager.dart';
|
||||
import 'package:hgdj/tools_base/debug_log.dart';
|
||||
import 'package:hgdj/tools_base/global_store/store.dart';
|
||||
import 'package:hgdj/tools_base/net/api_exception.dart';
|
||||
import 'package:hgdj/tools_base/net/net_manager.dart';
|
||||
import 'package:hgdj/tools_base/widget/common_alert.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import '../../alert/splash/line_error_dialog.dart';
|
||||
import '../../alert/splash/update_dialog.dart';
|
||||
import '../../hj_utils/free_play_manager.dart';
|
||||
import '../../tools_base/loading/loading_alert_widget.dart';
|
||||
import '../../track_event_manager/device_service.dart';
|
||||
import '../live/live_main_page.dart';
|
||||
import '../main_page/provider/msg_provider.dart';
|
||||
|
||||
//启动页
|
||||
class SplashLogic extends GetxController {
|
||||
// ===== 开屏广告 =====
|
||||
List<AdsInfoModel>? adsList; //开屏广告,没有就直接过
|
||||
bool isAdLoaded = false; //广告数据已就位(可能为空),页面据此决定要不要挂广告层
|
||||
bool isAdDone = false; //广告已放完/跳过,只等数据就绪
|
||||
|
||||
// ===== 启动数据 =====
|
||||
bool isDataReady = false; //模块列表已落地
|
||||
bool _isExiting = false; //强更被拒、已请求退出 app:后续流程一律不再放行首页
|
||||
|
||||
@override
|
||||
onReady() {
|
||||
super.onReady();
|
||||
_start();
|
||||
}
|
||||
|
||||
Future _start() async {
|
||||
netManager.clearUserAgent(); //清除老的ua
|
||||
await AdManager().preload(); //预热广告内存缓存,后续统一用 AdManager().adsByType
|
||||
_loadAds(isLast: true); //获取上次启动的广告
|
||||
final line = await _detectLine(); //选线
|
||||
//拿不到线路就到此为止:要么用户放弃了重试,要么弹框回调里重试成功、
|
||||
//已经自己走了一遍 _initByLine。这里再调一次会重复初始化
|
||||
if (TextUtil.isEmpty(line)) return;
|
||||
_initByLine(line!); //初始化流程
|
||||
}
|
||||
|
||||
//选到线路后的完整启动流程
|
||||
Future _initByLine(String line) async {
|
||||
//1. 初始化网络层
|
||||
_initNet(line);
|
||||
//2.先登录,后升级,不然渠道一升级就没有量了,先登录才能获取配置信息
|
||||
await _devLogin();
|
||||
// 模块列表只依赖登录拿到的 token,请求先发出去跟下面的配置/更新检查并行跑,
|
||||
// 但结果留到更新检查之后才放行(见末尾)——强更期间不能让首页变成可跳
|
||||
// ignore():先占个监听位,免得错误在 await 到之前被当成未捕获异常上报;真正的错误照旧在 await 处抛
|
||||
final tagTask = CommonService.getTagMarks()..ignore();
|
||||
//2. 获取远程配置
|
||||
final versions = await _fetchConfig();
|
||||
PreSaleProvider().refreshConfig();
|
||||
MineMsgProvider().startPayTimer(); //冷启动 /ping/domain 下发支付分层配置后启动倒计时
|
||||
FreePlayManager().refresh(); //免费次数进首页才用得到,不 await、不挡启动
|
||||
await _checkUpdate(versions); //强更时会停在启动页,必须挡在放行之前
|
||||
if (_isExiting) return; //强更被拒,app 正在退出,别再往下放行
|
||||
|
||||
_initServer(); // 初始化localserver缓存
|
||||
await _waitModules(tagTask); //放行跳首页
|
||||
}
|
||||
|
||||
///isLast = true 取出上次展示的广告
|
||||
Future _loadAds({bool isLast = true}) async {
|
||||
final list =
|
||||
isLast ? await AdManager().lastAdsByType(1) : AdManager().adsByType(1);
|
||||
if (AdManager().showAbTestAd) adsList = list;
|
||||
isAdLoaded = true;
|
||||
update();
|
||||
}
|
||||
|
||||
///一直检查网络直到成功
|
||||
Future<String?> _detectLine() async {
|
||||
LoadingAlertWidget.show(title: "选线中...");
|
||||
|
||||
final net = await Connectivity().checkConnectivity();
|
||||
// 有网络,开始选线
|
||||
if (net != ConnectivityResult.none) {
|
||||
final line = await DetectLineManager().detectLineOnce();
|
||||
LoadingAlertWidget.cancel();
|
||||
if (TextUtil.isNotEmpty(line)) return line;
|
||||
}
|
||||
await LineErrorDialog.show(callback: () async {
|
||||
final line = await DetectLineManager().detectLineOnce();
|
||||
LoadingAlertWidget.cancel();
|
||||
if (TextUtil.isNotEmpty(line)) _initByLine(line); //初始化流程
|
||||
});
|
||||
LoadingAlertWidget.cancel();
|
||||
return null;
|
||||
}
|
||||
|
||||
void _initNet(String host) {
|
||||
Address.baseHost = host;
|
||||
Address.baseApiPath = path.join(Address.baseHost ?? "", Address.apiPrefix);
|
||||
netManager.init(Address.baseApiPath ?? "");
|
||||
}
|
||||
|
||||
///登录
|
||||
Future<UserInfoModel?> _devLogin() async {
|
||||
String paste = "";
|
||||
//2.获取渠道码
|
||||
final channel = await DeviceInfoService.getChannel();
|
||||
if (TextUtil.isNotEmpty(channel)) {
|
||||
final traceId = await DeviceInfoService.fetchTraceId();
|
||||
final cutInfo = CutInfo()
|
||||
..dc = channel
|
||||
..tid = traceId;
|
||||
paste = json.encode(cutInfo.toJson());
|
||||
} else {
|
||||
//1.获取粘贴板(走缓存,避免 iOS 多次弹系统读取提示)
|
||||
final clipText = await DeviceInfoService.getClipboardTextCached();
|
||||
if (TextUtil.isNotEmpty(clipText)) {
|
||||
paste = clipText!;
|
||||
}
|
||||
}
|
||||
final deviceId = DeviceInfoService.deviceId;
|
||||
await netManager.refreshUserAgent();
|
||||
//登录 + 重试
|
||||
while (true) {
|
||||
final userInfo = await globalStore.loginByDevice(deviceId, paste: paste);
|
||||
|
||||
if (userInfo != null) return userInfo; //免广告策略已在 globalStore 登录成功时算好
|
||||
|
||||
final retry = await CommonAlert.show(
|
||||
content: "登录失败,是否重试?",
|
||||
confirmText: '重试',
|
||||
);
|
||||
|
||||
if (retry != true) return null;
|
||||
}
|
||||
}
|
||||
|
||||
//拉远端配置并落地,失败弹框重试;用户放弃就返回空版本列表(等于不检查更新)
|
||||
Future<List<CheckVersionInfo>> _fetchConfig() async {
|
||||
List<CheckVersionInfo>? versions;
|
||||
while (versions == null) {
|
||||
try {
|
||||
final info = await CommonService.fetchRemoteConfig();
|
||||
if (info == null) {
|
||||
// 配置拉取失败(无 token / 网络异常 / 返回体异常),走下面 catch 的重试逻辑
|
||||
throw Exception('获取远端配置为空');
|
||||
}
|
||||
await _applyDomains(info);
|
||||
await AdManager().saveAds(info.ads?.adsInfoList);
|
||||
|
||||
await _loadAds(isLast: false);
|
||||
Config.aiUndressPrice = info.aiUndressPrice ?? '';
|
||||
Config.aiVideoPrice = info.aiImageToVideoPrice ?? '';
|
||||
Config.aiDrawPrice = info.aiTextToImagePrice ?? '';
|
||||
Config.aiNovelPrice = info.aiTextToNovelPrice ?? '';
|
||||
Config.isStoreOpen = info.storeIsOpen ?? false;
|
||||
Config.jgArea = info.jgArea;
|
||||
versions = info.ver ?? [];
|
||||
//直播模块开关
|
||||
LiveMainPage.broadcast = info.broadcast ?? false;
|
||||
//预售相关
|
||||
presaleProvider.advanceStatus = info.advanceStatus;
|
||||
presaleProvider.advancePage = info.advancePage;
|
||||
//幸运抽奖相关
|
||||
presaleProvider.luckyDrawH5 = info.luckyDrawH5;
|
||||
presaleProvider.luckyDrawIcon = info.luckyDrawIcon;
|
||||
|
||||
//限时活动相关
|
||||
limitTimeProvider.bannerJump = info.bannerJump;
|
||||
Config.proxyBanner = info.proxyBannerJump;
|
||||
Config.hotWords = info.hotSearchTerms ?? [];
|
||||
Config.searchHints = info.searchHintWord ?? [];
|
||||
|
||||
Config.darkWebVipId = info.darkWebVipId;
|
||||
Config.darkWebVipName = info.darkWebVipName;
|
||||
Config.shortDramaCardId = info.shortDramaCardId;
|
||||
} catch (e) {
|
||||
debugLog('getRemoteConfig', e.toString());
|
||||
final retry = await CommonAlert.show(
|
||||
content: "获取配置信息失败${(e is ApiException) ? e.code : 1500},是否重试?",
|
||||
confirmText: '重试',
|
||||
);
|
||||
if (retry != true) break;
|
||||
}
|
||||
}
|
||||
return versions ?? [];
|
||||
}
|
||||
|
||||
//域名/公告落地
|
||||
Future _applyDomains(DomainSourceModel info) async {
|
||||
//域名列表信息
|
||||
for (final source in info.sourceList!) {
|
||||
switch (source.type) {
|
||||
case "IMAGE":
|
||||
Address.baseImagePath = source.domain![0].url;
|
||||
break;
|
||||
case "VID":
|
||||
Address.cdnAddressLists = source.domain!;
|
||||
Address.cdnAddress = Address.cdnAddressLists[0].url;
|
||||
break;
|
||||
case "GUIDE":
|
||||
Address.groundUrl = source.domain![0].url;
|
||||
break;
|
||||
case 'AUDIO':
|
||||
Address.audioCdnAddress = source.domain?.firstOrNull?.url;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AdManager().announceList = info.ads?.announList ?? [];
|
||||
}
|
||||
|
||||
///检查是否需要更新
|
||||
Future _checkUpdate(List<CheckVersionInfo> versions) async {
|
||||
//存内存,设置页的「检查更新」直接读这份比对,不再发请求
|
||||
saveVersion(versions);
|
||||
final target = checkUpdate();
|
||||
if (target == null) return;
|
||||
// 用户是否点了更新
|
||||
final isUpdate = await UpdateDialog.show(target);
|
||||
if (isUpdate != true && target.forcedUpdate == true) {
|
||||
//SystemNavigator.pop 只 finish Activity,engine 还能活几百毫秒,
|
||||
//这期间后面的流程会照跑、够把首页放行出去,所以打标记让调用方直接收尾
|
||||
_isExiting = true;
|
||||
await SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||
}
|
||||
}
|
||||
|
||||
//本地缓存服务:守护进程被内部 Timer 持有,不用再存字段
|
||||
Future _initServer() async {
|
||||
final server = CacheServer(cacheManager: null, openSubManager: true);
|
||||
await LocalServerGuard(server).run();
|
||||
//其余任意文件拦截
|
||||
server.addReqFilter(LOCAL_ALL_FILTER, Address.baseImagePath!);
|
||||
}
|
||||
|
||||
//模块数据落地 → 放行跳首页。请求在登录后就发出去了,这里只等结果。
|
||||
//拿不到模块一律不放行:首页 tab 全靠它,宁可停在启动页重试,也不进只剩"最新"的降级首页。
|
||||
//弹框只给"确定"一个选择,且不判返回值——点遮罩关掉也照样重试,不给静默卡死的出口
|
||||
Future _waitModules(Future<HomePlateModel?> task) async {
|
||||
var pending = task;
|
||||
while (true) {
|
||||
HomePlateModel? model;
|
||||
try {
|
||||
model = await pending;
|
||||
} catch (e) {
|
||||
//网络异常和 fromJson 异常都被网络层吞成 null,走不到这;兜的是请求前置(取token/签名)抛的异常
|
||||
debugLog('getTagMarks', e.toString());
|
||||
}
|
||||
if (model != null) {
|
||||
Config.plateModule = model;
|
||||
isDataReady = true;
|
||||
_tryJump();
|
||||
return;
|
||||
}
|
||||
await CommonAlert.show(content: "获取模块信息失败,请重试", showCancel: false);
|
||||
pending = CommonService.getTagMarks(); //旧 Future 已完成,重试必须重新发请求
|
||||
}
|
||||
}
|
||||
|
||||
//广告结束:标记可跳转、刷新出过渡遮罩,再尝试进首页
|
||||
void onAdFinish() {
|
||||
isAdDone = true;
|
||||
update();
|
||||
_tryJump();
|
||||
}
|
||||
|
||||
//广告和数据两边都就绪才进首页
|
||||
void _tryJump() {
|
||||
//opaque 在 offAll 里默认 false,不显式打开会在转场时透出下层
|
||||
if (isAdDone && isDataReady)
|
||||
Get.offAll(() => const MainPage(),
|
||||
binding: MainPageBinding(), opaque: true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// ignore_for_file: use_build_context_synchronously, depend_on_referenced_packages
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/assets_tool/app_colors.dart';
|
||||
import 'package:hgdj/assets_tool/images.dart';
|
||||
import 'package:hgdj/hj_page/splash/splash_ad_view.dart';
|
||||
import 'package:hgdj/hj_page/splash/splash_logic.dart';
|
||||
|
||||
import '../../config/config.dart';
|
||||
import '../../tools_base/loading/loading_alert_widget.dart';
|
||||
|
||||
class SplashPage extends StatelessWidget {
|
||||
static const routeName = '/SplashPage';
|
||||
const SplashPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
//这里用 GetBuilder 主要是预加载背景图
|
||||
body: GetBuilder<SplashLogic>(
|
||||
init: SplashLogic(),
|
||||
builder: (logic) => Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Image.asset(
|
||||
'ic_splash_bg.webp'.launchPath,
|
||||
fit: BoxFit.fill,
|
||||
width: Get.width,
|
||||
height: Get.height,
|
||||
),
|
||||
if (logic.isAdLoaded || logic.adsList != null)
|
||||
SplashAdView(
|
||||
adData: logic.adsList,
|
||||
callback: logic.onAdFinish,
|
||||
),
|
||||
const Positioned(
|
||||
bottom: 20,
|
||||
right: 20,
|
||||
child: Text(
|
||||
"V${Config.innerVersion}",
|
||||
style: TextStyle(
|
||||
color: AppColors.actionRed,
|
||||
fontSize: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
//广告先结束、首页数据还没就绪时的过渡遮罩
|
||||
if (logic.isAdDone) LoadingAlertWidget(title: '数据初始化中...'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user