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> routeObserver = RouteObserver>(); 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( 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(); } } }