hi-client/lib/main.dart
2026-01-07 17:33:55 -08:00

393 lines
14 KiB
Dart
Executable File
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:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
// import 'package:flutter_easyloading/flutter_easyloading.dart'; // 已替换为自定义组件
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter/services.dart';
// import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:get/get.dart';
// import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:kaer_with_panels/app/themes/kr_theme_service.dart';
import 'package:kaer_with_panels/app/localization/getx_translations.dart';
import 'package:kaer_with_panels/app/localization/kr_language_utils.dart';
import 'package:kaer_with_panels/app/routes/app_pages.dart';
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
import 'package:kaer_with_panels/app/utils/kr_window_manager.dart';
import 'app/services/singbox_imp/kr_sing_box_imp.dart';
import 'app/common/app_run_data.dart';
import 'app/utils/kr_secure_storage.dart';
import 'app/common/app_config.dart';
import 'app/services/kr_site_config_service.dart';
import 'app/services/global_overlay_service.dart';
import 'app/utils/kr_log_util.dart';
import 'app/utils/kr_secure_storage.dart';
import 'app/utils/kr_init_log_collector.dart';
import 'app/utils/kr_file_logger.dart';
import 'package:kaer_with_panels/app/routes/transitions/slide_transparent_transition.dart';
import 'package:kaer_with_panels/app/routes/transitions/transition_config.dart';
// 全局导航键
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
void main() async {
// 🔧 全局异常捕获:捕获所有未处理的异常
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
// 🔧 初始化日志收集器(必须最先初始化)
final logCollector = KRInitLogCollector();
// 🔧 P6修复: 必须await完成初始化,否则日志会丢失
await logCollector.initialize();
logCollector.log('🚀 应用启动', tag: 'MAIN');
// 🔧 诊断信息: 记录平台和设备信息
logCollector.log('平台: ${Platform.operatingSystem}', tag: 'DEVICE');
logCollector.log('系统版本: ${Platform.operatingSystemVersion}', tag: 'DEVICE');
logCollector.log('Dart版本: ${Platform.version}', tag: 'DEVICE');
// 🔧 Flutter框架异常捕获
FlutterError.onError = (FlutterErrorDetails details) {
logCollector.logError(
'Flutter框架异常: ${details.exception}',
tag: 'FLUTTER_ERROR',
error: details.exception,
stackTrace: details.stack,
);
FlutterError.presentError(details);
};
// 🔧 异步异常捕获PlatformDispatcher
PlatformDispatcher.instance.onError = (error, stack) {
logCollector.logError(
'平台异步异常: $error',
tag: 'PLATFORM_ERROR',
error: error,
stackTrace: stack,
);
return true;
};
await _initializeApp(logCollector);
}, (error, stack) {
// 🔧 Zone捕获的未处理异常
final logCollector = KRInitLogCollector();
logCollector.logError(
'Zone未处理异常: $error',
tag: 'ZONE_ERROR',
error: error,
stackTrace: stack,
);
});
}
Future<void> _initializeApp(KRInitLogCollector logCollector) async {
try {
logCollector.logPhaseStart('初始化核心服务');
// 🔧 初始化文件日志系统(用于诊断 UI 卡死问题)
logCollector.log('初始化文件日志系统', tag: 'FILE_LOGGER');
await KRFileLogger.initialize();
await KRFileLogger.log('📝 文件日志系统已启动');
logCollector.logSuccess('文件日志系统初始化完成', tag: 'FILE_LOGGER');
// 为所有 HttpClient 请求统一注入代理策略
logCollector.log('配置 HTTP 代理策略', tag: 'HTTP');
HttpOverrides.global = KRProxyHttpOverrides();
// 初始化 Hive
logCollector.log('初始化 Hive 数据库', tag: 'HIVE');
await KRSecureStorage().kr_initHive();
logCollector.logSuccess('Hive 初始化完成', tag: 'HIVE');
// 初始化主题
logCollector.log('初始化主题服务', tag: 'THEME');
await KRThemeService().init();
logCollector.logSuccess('主题初始化完成', tag: 'THEME');
// 初始化翻译
logCollector.log('加载多语言翻译', tag: 'I18N');
final translations = GetxTranslations();
await translations.loadAllTranslations();
logCollector.logSuccess('翻译加载完成', tag: 'I18N');
// 获取最后保存的语言
final initialLocale = await KRLanguageUtils.getLastSavedLocale();
logCollector.log('使用语言: ${initialLocale.toString()}', tag: 'I18N');
logCollector.logPhaseEnd('初始化核心服务', success: true);
// 启动域名预检测(异步,不阻塞应用启动)
logCollector.log('启动域名预检测', tag: 'DOMAIN');
KRDomain.kr_preCheckDomains();
logCollector.log('✅ 核心服务初始化完成,启动应用', tag: 'MAIN');
// 🔧 P9修复: 不要立即finalize,让日志文件保持打开以收集后续信息
// finalize会在应用关闭或_AppLifecycleWrapper的dispose时调用
// 初始化全局 Overlay 服务
Get.put(GlobalOverlayService());
// 🔧 关键修复:必须先 runApp(),让 Flutter 引擎启动
// 窗口管理器初始化移到 runApp 之后,通过 WidgetsBinding.addPostFrameCallback 延迟执行
// 这样可以确保窗口显示时 Flutter UI 已经渲染完成
runApp(_myApp(translations, initialLocale));
// 🔧 在 Flutter 引擎启动后再初始化窗口管理器
if (Platform.isMacOS || Platform.isWindows) {
// 使用 addPostFrameCallback 确保在首帧渲染后再显示窗口
WidgetsBinding.instance.addPostFrameCallback((_) async {
logCollector.logPhaseStart('初始化窗口管理器');
await KRWindowManager().kr_initWindowManager();
logCollector.logPhaseEnd('初始化窗口管理器', success: true);
});
}
} catch (error, stackTrace) {
logCollector.logError(
'应用初始化失败',
tag: 'INIT_FATAL',
error: error,
stackTrace: stackTrace,
);
await logCollector.finalize();
rethrow;
}
}
Widget _myApp(GetxTranslations translations, Locale initialLocale) {
return _AppLifecycleWrapper(
child: GetMaterialApp(
navigatorKey: navigatorKey, // 使用全局导航键
title: "Hi快VPN",
initialRoute: Routes.KR_SPLASH,
getPages: AppPages.routes,
builder: (context, child) {
// 🔧 P2修复: 将ScreenUtil初始化移到StatefulWidget中,避免重复初始化
return _ScreenUtilInitializer(
child: child ?? Container(),
);
},
theme: KRThemeService().kr_lightTheme(),
darkTheme: KRThemeService().kr_darkTheme(),
themeMode: KRThemeService().kr_Theme,
translations: translations,
locale: initialLocale,
fallbackLocale: const Locale('zh', 'CN'),
debugShowCheckedModeBanner: false,
transitionDuration: TransitionConfig.defaultDuration, // 设置动画持续时间
customTransition: TransitionConfig.createDefaultTransition(),
routingCallback: (routing) {
if (routing == null) return;
if(Routes.KR_PURCHASE_MEMBERSHIP.contains(routing.current)) return;
// 需要显示订阅按钮的路由列表
const showButtonRoutes = [
Routes.MR_LOGIN,
Routes.HI_MENU,
Routes.KR_HOME,
Routes.HI_USER_INFO,
Routes.KR_ORDER_STATUS,
];
print('routing.current${routing.current}');
GlobalOverlayService.instance.updateSubscriptionButtonColor(null);
if (showButtonRoutes.contains(routing.current)) {
GlobalOverlayService.instance.safeShowSubscriptionButton();
} else {
GlobalOverlayService.instance.hideSubscriptionButton();
}
},
)
);
}
/// 🔧 P2修复: ScreenUtil初始化包装器,避免重复初始化
/// 🔧 P0修复: 使用静态变量确保全局只初始化一次防止Widget重建导致的状态丢失
class _ScreenUtilInitializer extends StatefulWidget {
final Widget child;
const _ScreenUtilInitializer({required this.child});
@override
State<_ScreenUtilInitializer> createState() => _ScreenUtilInitializerState();
}
class _ScreenUtilInitializerState extends State<_ScreenUtilInitializer> {
// 🔧 P0修复: 使用静态变量确保即使Widget重建也不会重复初始化
static bool _isGlobalInitialized = false;
// 🔧 P0修复: 记录初始化时的屏幕尺寸,用于检测屏幕变化
static Size? _lastScreenSize;
@override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
final currentSize = mediaQuery.size;
// 🔧 P0修复: 只在以下情况初始化:
// 1. 从未初始化过
// 2. 屏幕尺寸发生显著变化(如旋转屏幕)
final needsInit = !_isGlobalInitialized ||
(_lastScreenSize != null &&
((_lastScreenSize!.width - currentSize.width).abs() > 50 ||
(_lastScreenSize!.height - currentSize.height).abs() > 50));
if (needsInit) {
// 🔧 P9修复: 记录屏幕信息(延迟记录,确保能写入文件)
if (kDebugMode || AppConfig.enableInitLogCollection) {
// 延迟记录,确保在UI线程空闲时执行
WidgetsBinding.instance.addPostFrameCallback((_) {
final logCollector = KRInitLogCollector();
logCollector.log('━━━ 屏幕信息诊断 ━━━', tag: 'SCREEN');
logCollector.log('屏幕尺寸: ${mediaQuery.size.width} x ${mediaQuery.size.height}', tag: 'SCREEN');
logCollector.log('设备像素比: ${mediaQuery.devicePixelRatio}', tag: 'SCREEN');
logCollector.log('文本缩放: ${mediaQuery.textScaler.scale(1.0)}', tag: 'SCREEN');
logCollector.log('安全区域: top=${mediaQuery.padding.top}, bottom=${mediaQuery.padding.bottom}', tag: 'SCREEN');
});
}
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
// 🔧 修复:设计尺寸改为实际窗口尺寸 800x668确保字体和UI元素正确缩放
ScreenUtil.init(context,
designSize: const Size(800, 668), minTextAdapt: true);
if (kDebugMode || AppConfig.enableInitLogCollection) {
KRInitLogCollector().log('ScreenUtil初始化: 桌面模式 800x668', tag: 'SCREEN');
}
} else {
ScreenUtil.init(context,
designSize: const Size(375, 667), minTextAdapt: true);
if (kDebugMode || AppConfig.enableInitLogCollection) {
KRInitLogCollector().log('ScreenUtil初始化: 移动模式 375x667', tag: 'SCREEN');
}
}
_isGlobalInitialized = true;
_lastScreenSize = currentSize;
}
// 如果是 Mac 平台,添加顶部安全区域
Widget wrappedChild = widget.child;
if (Platform.isMacOS) {
wrappedChild = MediaQuery(
data: MediaQuery.of(context).copyWith(
padding: MediaQuery.of(context).padding.copyWith(
top: 10.w, // Mac 平台顶部安全区域
),
),
child: wrappedChild,
);
}
return Stack(
fit: StackFit.expand,
children: [
// 背景层:使用您的自定义组件
const KrLocalImage(
imageName: 'global-bg',
imageType: ImageType.jpg,
fit: BoxFit.cover, // 确保背景覆盖整个屏幕
),
// 内容层
wrappedChild,
Overlay(
initialEntries: [
OverlayEntry(builder: (_) => const SizedBox.shrink()), // 初始化空 Overlay
],
),
],
);
}
}
/// 🔧 P3修复: 应用生命周期监听包装器
class _AppLifecycleWrapper extends StatefulWidget {
final Widget child;
const _AppLifecycleWrapper({required this.child});
@override
State<_AppLifecycleWrapper> createState() => _AppLifecycleWrapperState();
}
class _AppLifecycleWrapperState extends State<_AppLifecycleWrapper>
with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
// 🔧 P9修复: 应用关闭时finalize日志文件
KRInitLogCollector().finalize();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (kDebugMode) {
print('🔄 应用生命周期变化: $state');
}
// 🔧 P3修复: 当应用从后台恢复时,重置关键服务
if (state == AppLifecycleState.resumed) {
if (kDebugMode) {
print('♻️ 应用恢复,开始重置关键服务...');
}
_resetCriticalServices();
}
}
/// 🔧 P3修复: 重置关键服务,防止状态污染
Future<void> _resetCriticalServices() async {
try {
// 🔧 P1修复: 重置主题服务
await KRThemeService().reset();
if (kDebugMode) {
print('✅ 主题服务已重置');
}
// 🔧 P1修复: 重置应用运行时数据
await KRAppRunData.getInstance().kr_resetRuntimeState();
if (kDebugMode) {
print('✅ 应用运行时数据已重置');
}
// 🔧 P1修复: 清理域名检测状态
KRDomain.kr_resetDomainState();
if (kDebugMode) {
print('✅ 域名状态已重置');
}
// 刷新UI
if (mounted) {
setState(() {});
}
} catch (e) {
if (kDebugMode) {
print('⚠️ 服务重置失败: $e');
}
}
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
/// 全局 HttpOverrides确保所有 dart:io 网络请求遵循 sing-box 代理策略
class KRProxyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
final client = super.createHttpClient(context);
client.findProxy = (uri) => KRSingBoxImp.instance.kr_buildProxyRule();
return client;
}
}