import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:catcher_2/catcher_2.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:mom_kitchen/src/l10n/app_localizations.dart'; import 'package:mom_kitchen/src/config/app_routes.dart'; import 'package:mom_kitchen/src/services/core/app_service.dart'; import 'package:mom_kitchen/src/services/api/api_service.dart'; import 'package:mom_kitchen/src/services/system/orientation_service.dart'; import 'package:mom_kitchen/src/services/ui/theme_service.dart'; import 'package:mom_kitchen/src/services/ui/toast_service.dart'; import 'package:mom_kitchen/src/services/data/storage/storage_service.dart'; import 'package:mom_kitchen/src/services/system/crash_guard_service.dart'; import 'package:mom_kitchen/src/app_binding.dart'; import 'package:mom_kitchen/src/utils/app_logger.dart'; // 2026-04-15 | main.dart | 应用入口 | Catcher2最先初始化+串行化启动流程防ANR // 2026-04-15 | 增强防御:所有初始化步骤加超时+try-catch,任何一步失败都不阻断启动 // 2026-04-15 | Trae IDE兼容:增强错误处理,防止调试环境闪退 void main() { runZonedGuarded( () { final crashGuard = CrashGuardService(); Catcher2( runAppFunction: () async { WidgetsFlutterBinding.ensureInitialized(); PlatformDispatcher.instance.onError = (error, stack) { debugPrint('🚨 PlatformDispatcher Error: $error'); try { Catcher2.reportCheckedError(error, stack); } catch (_) { debugPrint('❌ PlatformDispatcher report failed: $error'); } return true; }; FlutterError.onError = (details) { FlutterError.presentError(details); try { Catcher2.reportCheckedError(details.exception, details.stack); } catch (_) { debugPrint( '❌ FlutterError.onError catch failed: ${details.exception}', ); } }; try { await _initApp(); } catch (e, st) { debugPrint('🚨 _initApp failed: $e'); debugPrint('Stack: $st'); } runApp(const MyApp()); }, ensureInitialized: false, debugConfig: crashGuard.buildDebugOptions(), releaseConfig: crashGuard.buildReleaseOptions(), ); }, (error, stack) { debugPrint('🚨 Uncaught async error: $error'); debugPrint('Stack: $stack'); }, ); } /// 启动初始化链 - 每一步都有独立超时和try-catch /// 核心原则:任何一步失败都不阻断后续步骤,保证App至少能启动 Future _initApp() async { // 0. 加载环境变量(含SMTP等敏感配置,不阻断启动) try { await dotenv .load(fileName: '.env') .timeout( const Duration(seconds: 3), onTimeout: () => debugPrint('⚠️ .env加载超时(3s)'), ); } catch (e) { debugPrint('⚠️ .env加载失败(使用默认空值): $e'); } // 1. 核心服务初始化(最关键,但不阻断) try { await AppService.instance.init().timeout( const Duration(seconds: 10), onTimeout: () => debugPrint('❌ AppService初始化超时(10s)'), ); } catch (e) { debugPrint('❌ AppService初始化失败: $e'); } // 2. DNS预检(非关键,纯后台) try { await ApiService().preCheckDns().timeout( const Duration(seconds: 5), onTimeout: () => debugPrint('⚠️ DNS预检超时(5s)'), ); } catch (e) { debugPrint('⚠️ DNS预检失败: $e'); } // 3. CrashGuard初始化 try { await CrashGuardService.init().timeout(const Duration(seconds: 3)); } catch (e) { debugPrint('⚠️ CrashGuardService初始化失败或超时: $e'); } if (kDebugMode) { AppRoutes.registerAllPages(); } await OrientationService().unlockOrientation(); } class MyApp extends StatelessWidget { const MyApp({super.key}); ThemeService _getThemeService() { if (Get.isRegistered()) { return Get.find(); } return Get.put(ThemeService.instance, permanent: true); } @override Widget build(BuildContext context) { final themeService = _getThemeService(); return Obx(() { final textScale = themeService.fontSize.value / 16.0; return GetCupertinoApp( title: 'Mom\'s Kitchen', navigatorKey: Catcher2.navigatorKey, theme: themeService.cupertinoThemeData, locale: Locale(themeService.currentLocale.value), debugShowCheckedModeBanner: false, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: const [ Locale('en'), Locale('zh'), Locale('zh', 'Hant'), ], getPages: AppRoutes.pages, initialBinding: AppBinding(), builder: (context, widget) { return MediaQuery( data: MediaQuery.of( context, ).copyWith(textScaler: TextScaler.linear(textScale)), child: ColoredBox( color: themeService.backgroundColor.value, child: widget ?? const SizedBox.shrink(), ), ); }, routingCallback: (routing) { if (kDebugMode && routing?.current != null) { AppLogger.d('🔍 路由变化: ${routing?.previous} → ${routing?.current}'); } }, home: const _InitWrapper(), ); }); } } class _InitWrapper extends StatefulWidget { const _InitWrapper(); @override State<_InitWrapper> createState() => _InitWrapperState(); } class _InitWrapperState extends State<_InitWrapper> with WidgetsBindingObserver { bool _navigated = false; int _retryCount = 0; static const int _maxRetries = 5; String? _lastError; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addPostFrameCallback((_) { _navigateToMain(); }); } void _navigateToMain() { if (!mounted || _navigated) return; try { ToastService.init(context); final agreementAccepted = StorageService().getBool('agreement_accepted') ?? false; if (agreementAccepted) { Get.offAllNamed(AppRoutes.main); } else { Get.offAllNamed(AppRoutes.guide); } _navigated = true; debugPrint('✅ 导航到${agreementAccepted ? '主页面' : '引导页'}成功'); } catch (e, st) { _lastError = e.toString(); _retryCount++; debugPrint('❌ 导航失败 (第$_retryCount次): $e'); debugPrint('Stack: $st'); if (_retryCount < _maxRetries) { Future.delayed(Duration(milliseconds: 300 * _retryCount), () { if (mounted && !_navigated) _navigateToMain(); }); } else { debugPrint('🚨 导航重试已达上限,显示错误页面'); if (mounted) setState(() {}); } } } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangePlatformBrightness() { super.didChangePlatformBrightness(); final brightness = WidgetsBinding.instance.platformDispatcher.platformBrightness; try { final themeService = Get.find(); themeService.onSystemBrightnessChanged(brightness); } catch (_) {} } @override Widget build(BuildContext context) { if (_retryCount >= _maxRetries) { return CupertinoPageScaffold( child: SafeArea( child: Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon( CupertinoIcons.exclamationmark_triangle, size: 48, color: CupertinoColors.systemRed, ), const SizedBox(height: 16), const Text( '启动失败,请重试', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), ), if (_lastError != null) ...[ const SizedBox(height: 12), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: CupertinoColors.systemGrey5, borderRadius: BorderRadius.circular(8), ), child: Text( _lastError!.length > 200 ? '${_lastError!.substring(0, 200)}...' : _lastError!, style: const TextStyle( fontSize: 12, fontFamily: 'monospace', ), textAlign: TextAlign.center, ), ), ], const SizedBox(height: 24), Row( mainAxisSize: MainAxisSize.min, children: [ CupertinoButton( onPressed: () { _retryCount = 0; _lastError = null; setState(() {}); _navigateToMain(); }, child: const Text('重试'), ), const SizedBox(width: 16), CupertinoButton.filled( onPressed: () { _retryCount = 0; _lastError = null; _navigated = false; setState(() {}); _navigateToMain(); }, child: const Text('强制重启'), ), ], ), ], ), ), ), ), ); } return const CupertinoPageScaffold( child: Center(child: CupertinoActivityIndicator()), ); } }