Files
huangguo_android/lib/main.dart
T
2026-09-15 15:44:13 +07:00

203 lines
9.7 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:hgdj/hj_page/main_page/provider/bottom_bar_provider.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_page/splash/splash_page.dart';
import 'package:hgdj/hj_utils/codec_support.dart';
import 'package:hgdj/hj_utils/video_view_type.dart';
import 'package:hgdj/routers/router_map.dart';
import 'package:visibility_detector/visibility_detector.dart';
import 'package:hgdj/tools_base/debug_log.dart';
import 'package:hgdj/tools_base/global_store/store.dart';
import 'package:hgdj/tools_base/headphone_monitor.dart';
import 'package:hgdj/tools_base/net/http_manager.dart';
import 'package:hgdj/tools_base/video_download/video_download_manager.dart';
import 'package:hgdj/track_event_manager/device_service.dart';
import 'package:hgdj/track_event_manager/track_session.dart';
// 只引 OKToast 挂载所需符号:oktoast 也导出同名 showToast,全量 import 会与自家 toast.dart 冲突
import 'package:oktoast/oktoast.dart' show OKToast, ToastPosition;
import 'package:provider/provider.dart';
import 'package:ume_core/ume_core.dart';
import 'package:ume_kit_dio/ume_kit_dio.dart';
import 'package:ume_kit_perf/ume_kit_perf.dart';
import 'package:ume_kit_ui/ume_kit_ui.dart';
import 'alert/vip_guide/guide_countdown_dialog.dart';
import 'assets_tool/app_colors.dart';
import 'hj_page/home/provider/home_update_marker_provider.dart';
import 'hj_page/main_page/provider/msg_provider.dart';
import 'hj_page/mine/mine_vip/vip_product_manager.dart';
import 'hj_utils/light_model.dart';
final RouteObserver<ModalRoute<void>> routeObserver =
RouteObserver<ModalRoute<void>>();
void main() async {
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
// 可见性回调默认攒 500ms 才发,短视频切 tab 会拖半秒才静音;压到 100ms(听不出延迟)。
// 不设 0:那样滚动中每帧都回调
VisibilityDetectorController.instance.updateInterval =
const Duration(milliseconds: 100);
VideoDownloadManager.instance.init();
await lightKV.init(); //初始化本地存储(lightKV/MMKV),其余初始化都依赖它,必须最先
httpManager.initDefault();
// 以下五项只读本地 KV / 设备信息、彼此无依赖,并行执行缩短冷启动首帧前耗时
await Future.wait([
globalStore.init(), //获取密码
DeviceInfoService.init(), //获取设备信息
initVideoViewType(), //视频渲染方式:读本机持久化标记,决定 textureView/platformView
CodecSupport.init(), //H.265 硬解能力:查芯片能力 + 读本机禁用标记,决定选 265 还是回落 264
]);
await TrackSessionManager()
.onAppColdStart(); //初始化请求头 sid(依赖 DeviceInfoService
WidgetsBinding.instance.addObserver(_TrackLifecycleObserver());
PromoCountdown.reset(); //优惠倒计时(场景3/4):app 启动时设定一次起点,全局仅此一次,进程存活期间自然倒数、不重置
HeadphoneMonitor.instance.start(); //耳机/蓝牙断开、来电等自动暂停视频,防止音频外放泄露隐私
if (kReleaseMode) {
runApp(const MyApp());
} else {
PluginManager.instance
..register(DioInspector(dio: httpManager.dio))
..register(Performance())
..register(const WidgetInfoInspector())
..register(const WidgetDetailInspector());
runApp(UMEWidget(child: const MyApp()));
}
}, (exception, stackTrace) async {
debugLog('exception = $exception\n stackTrace = $stackTrace');
});
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
PaintingBinding.instance.imageCache.maximumSize = 1000;
PaintingBinding.instance.imageCache.maximumSizeBytes = 500 << 20;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light.copyWith(
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.black,
systemNavigationBarIconBrightness: Brightness.light,
//Android 10+ 默认会给透明的状态栏/导航栏自动垫一层半透明黑(contrast scrim)
//导致短视频等全屏页上下多出一层压暗、且各家 ROM 表现不一,显式关掉统一成无遮罩。
//改这两个要同步下面 AppBarTheme.systemOverlayStyle,那边会覆盖本处设置
systemStatusBarContrastEnforced: false,
systemNavigationBarContrastEnforced: false,
),
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
// 触摸收起键盘
FocusManager.instance.primaryFocus?.unfocus();
},
child: ScreenUtilInit(
designSize: const Size(428, 927),
minTextAdapt: true,
splitScreenMode: true,
builder: (context, child) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: PreSaleProvider()), //预售
ChangeNotifierProvider.value(value: globalStore),
ChangeNotifierProvider.value(value: HomeBottomProvider()),
ChangeNotifierProvider.value(value: DarkwebBottomProvider()),
ChangeNotifierProvider.value(value: CommunityBottomProvider()),
ChangeNotifierProvider.value(
value: MineMsgProvider()), //消息中心红点,新人倒计时
ChangeNotifierProvider.value(
value: HomeUpdateMarkerProvider()), //首页最新/今日最新红点
ChangeNotifierProvider.value(
value: vipProductManager), //会员卡列表统一数据源
ChangeNotifierProvider.value(value: LimitTimeProvider()), //限时活动
],
child: GetMaterialApp(
// controller 只由 GetBuilder 回收,不跟路由绑定。默认的 full 会把它挂到「注册那刻的栈顶路由」,
// 弹窗期间新建的 GetBuilder 就被记到弹窗名下,弹窗一关 logic 被删、widget 还在(播放器因此永久转圈)。
// 只能写在这,写 main() 里会被 GetMaterialApp.build 覆盖回 full。
// 前提是所有 logic 都走 GetBuilder(init:)——手写 Get.put 的没人回收,要自己 Get.delete
smartManagement: SmartManagement.onlyBuilder,
navigatorObservers: [routeObserver],
debugShowCheckedModeBanner: true,
initialRoute: SplashPage.routeName,
getPages: AppRoutes.routePages,
theme: ThemeData(
platform: TargetPlatform.iOS,
scaffoldBackgroundColor: Color(0xff0F0F0F),
primaryColor: AppColors.primaryColor,
unselectedWidgetColor: Color(0xffD8D8D8),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
tabBarTheme: const TabBarThemeData(
dividerColor: Colors.transparent,
splashFactory: NoSplash.splashFactory,
),
textSelectionTheme: TextSelectionThemeData(
cursorColor: AppColors.primaryHighColor,
),
appBarTheme: AppBarTheme(
centerTitle: true,
iconTheme: IconThemeData(color: Colors.white, size: 18),
shadowColor: Colors.transparent,
elevation: 0,
scrolledUnderElevation: 0.0,
backgroundColor: Colors.black,
titleTextStyle: TextStyle(
color: Color(0xE5FFFFFF),
fontSize: 18,
fontWeight: FontWeight.w600,
),
//有 AppBar 的页面由 AppBar 自己下发 overlay style,会盖掉最外层
//AnnotatedRegion 的设置,两个 contrastEnforced 必须跟那边保持一致
systemOverlayStyle: SystemUiOverlayStyle.light.copyWith(
systemStatusBarContrastEnforced: false,
systemNavigationBarContrastEnforced: false,
),
),
// 全局 BackButton 用 iOS 经典风格箭头(与项目历史 BaseAppBar 视觉一致)
actionIconTheme: ActionIconThemeData(
backButtonIconBuilder: (context) =>
const Icon(Icons.arrow_back_ios),
),
),
builder: (context, child) => OKToast(
position: ToastPosition.center,
backgroundColor: Colors.black.withValues(alpha: .85),
radius: 8,
textStyle: const TextStyle(color: Colors.white, fontSize: 14),
textPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: child!,
),
),
);
},
),
),
);
}
}
class _TrackLifecycleObserver extends WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused ||
state == AppLifecycleState.inactive ||
state == AppLifecycleState.detached) {
TrackSessionManager().onAppPause();
} else if (state == AppLifecycleState.resumed) {
TrackSessionManager().onAppResume();
}
}
}