// ============================================================ // 闲言APP — 应用入口 // 创建时间: 2026-04-20 /// 更新时间: 2026-06-05 /// 作用: main 函数,初始化存储 + 液态玻璃 + 异常捕获 + 启动 App /// 上次更新: 新增PlatformCapabilities.init()平台能力注册表初始化 // ============================================================ import 'dart:async'; import 'dart:io'; import 'dart:isolate' if (dart.library.js) 'core/utils/platform/isolate_stub.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:liquid_glass_widgets/liquid_glass_widgets.dart'; import 'package:logging/logging.dart'; import 'package:flutter/services.dart'; import 'package:window_manager/window_manager.dart'; import 'app/app.dart'; import 'core/constants/app_constants.dart'; import 'core/services/network/deep_link_service.dart'; import 'core/router/deep_link_resolver.dart'; import 'core/services/performance/performance_orchestrator.dart'; import 'core/services/error/crash_monitor.dart'; import 'core/storage/kv_storage.dart'; import 'core/services/error/global_error_handler.dart'; import 'core/services/post_agreement_initializer.dart'; import 'core/utils/logger.dart' show Log, LogCategory; import 'core/utils/platform/platform_utils.dart' as pu; import 'core/utils/platform/platform_capability.dart'; import 'core/utils/platform/clipboard_bridge.dart'; import 'core/registry/page_registry.dart'; import 'core/router/app_router.dart'; import 'core/services/catcher2_config_service.dart'; import 'editor/services/3d/platform_3d_service.dart'; import 'features/template/services/wallpaper_favorite_service.dart'; import 'features/template/services/wallpaper_health_service.dart'; import 'features/settings/services/settings_change_logger.dart'; import 'features/auth/providers/auth_provider.dart'; import 'features/discover/services/rss_service.dart'; import 'features/discover/services/exchange_rate_service.dart'; import 'package:path_provider/path_provider.dart'; bool kvStorageReady = false; bool _liquidGlassReady = false; bool get liquidGlassReady => _liquidGlassReady; void main() async { // Catcher2 内部会调用 runZonedGuarded 并在其中执行 runApp, // 因此 binding 必须在 Catcher2 之前初始化(在同一个 root zone), // 否则会出现 Zone mismatch 警告。 WidgetsFlutterBinding.ensureInitialized(); // 移除外层 runZonedGuarded,由 Catcher2 内部管理 zone // 异常捕获由 Catcher2 + GlobalErrorHandler 共同处理 _appMain().catchError((Object e, StackTrace st) { Log.e('main初始化失败', e, st, LogCategory.general); }); } /// 应用主入口逻辑 Future _appMain() async { if (pu.isDesktop) { await windowManager.ensureInitialized(); const windowOptions = WindowOptions( minimumSize: Size(400, 600), title: '闲言', center: true, // 隐藏系统标题栏,使用自定义软件样式标题栏(DesktopWindowTitleBar) titleBarStyle: TitleBarStyle.hidden, // macOS: 隐藏原生红黄绿按钮,由 Flutter 侧自绘 skipTaskbar: false, ); await windowManager.waitUntilReadyToShow(windowOptions, () async { await windowManager.show(); }); } if (pu.isOhos) Log.i('🟢 [OHOS] main() 开始执行', null, null, LogCategory.general); // 初始化平台能力注册表(必须在其他服务初始化之前) PlatformCapabilities.init(); Log.i('平台能力注册表初始化完成', null, null, LogCategory.general); // 鸿蒙端安装标准剪贴板拦截器,修复输入框粘贴不工作 // 原生端EntryAbility.ets拦截flutter/platform通道的Clipboard方法 ClipboardBridge.installOhosClipboardInterceptor(); FlutterError.onError = (FlutterErrorDetails details) { final msg = details.exceptionAsString(); // 布局溢出等非致命错误:仅打印日志,不弹窗/不显示溢出指示器 if (msg.contains('overflowed')) { Log.w( '⚠️ FlutterError: RenderFlex overflow (已静默)', msg, details.stack, LogCategory.general, ); return; } // IOSScrollViewFlingVelocityTracker 时间戳乱序:Flutter框架已知bug,非致命 // iOS上触摸事件时间戳偶尔乱序导致velocity tracker断言失败,不影响功能 if (msg.contains('smaller timestamp') && msg.contains('predecessor')) { Log.w( '⚠️ FlutterError: VelocityTracker timestamp out-of-order (已静默)', msg, details.stack, LogCategory.ui, ); return; } FlutterError.presentError(details); Log.e( '🔥 FlutterError.onError', details.exceptionAsString(), details.stack, LogCategory.general, ); }; GlobalErrorHandler.init(); if (!pu.isWeb) { Isolate.current.addErrorListener( RawReceivePort((Object? pair) { final List errorAndStacktrace = pair as List; Log.e( '🔥 Isolate uncaught error', errorAndStacktrace.first, StackTrace.fromString(errorAndStacktrace.last.toString()), LogCategory.general, ); }).sendPort, ); } if (!pu.isWeb) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); } if (pu.isOhos) Log.i('🟢 [OHOS] SystemChrome 配置完成', null, null, LogCategory.general); try { await AppVersion.init(); Log.i('AppVersion 初始化完成', null, null, LogCategory.general); } catch (e, st) { Log.e('AppVersion 初始化失败', e, st, LogCategory.general); } try { await KvStorage.init(); kvStorageReady = true; if (pu.isOhos) Log.i('🟢 [OHOS] KvStorage 初始化完成', null, null, LogCategory.storage); } catch (e, st) { Log.e('KV 存储初始化失败', e, st, LogCategory.storage); } try { // HapticService.init() 已移至 PostAgreementInitializer // Vibrate.canVibrate 会触发 flutter_vibrate 原生插件初始化, // 未同意隐私协议前不应调用 Log.i('触觉反馈服务延迟到协议同意后初始化', null, null, LogCategory.haptic); } catch (e, st) { Log.e('触觉反馈服务初始化失败', e, st, LogCategory.haptic); } try { await WallpaperFavoriteService.init(); Log.i('壁纸收藏服务初始化完成', null, null, LogCategory.general); } catch (e, st) { Log.e('壁纸收藏服务初始化失败', e, st, LogCategory.general); } try { PerformanceOrchestrator.instance.init(); Log.i('PerformanceOrchestrator 初始化完成', null, null, LogCategory.service); } catch (e, st) { Log.e('PerformanceOrchestrator 初始化失败', e, st, LogCategory.service); } try { await LiquidGlassWidgets.initialize(); _liquidGlassReady = true; Log.i( 'LiquidGlassWidgets 初始化完成 (ohos=${pu.isOhos})', null, null, LogCategory.ui, ); } catch (e, st) { Log.e('LiquidGlassWidgets 初始化失败', e, st, LogCategory.ui); } // 抑制 liquid_glass_widgets 内部 lgr 系列日志(FINER 级别高频输出) // 仅在 debug 模式下抑制,release 模式本身不会输出这些日志 _suppressLiquidGlassLogs(); try { await Platform3DService.instance.detectDeviceCapability(); if (pu.isOhos) Log.i( '🟢 [OHOS] 3D设备检测完成 (lowEnd=${Platform3DService.instance.isLowEnd})', null, null, LogCategory.device, ); } catch (e, st) { Log.e('3D平台设备检测失败', e, st, LogCategory.device); } try { _validatePageRegistry(); } catch (e, st) { Log.e('页面注册表验证失败', e, st, LogCategory.general); } if (!pu.isWeb) { // Debug 模式下验证深度链接配置完整性 assert(() { final errors = DeepLinkResolver.validate(); if (errors.isNotEmpty) { for (final e in errors) { Log.e('🔗 [DeepLink] 配置错误: $e', null, null, LogCategory.router); } } else { Log.i('🔗 [DeepLink] 配置验证通过', null, null, LogCategory.router); } return true; }()); try { await DeepLinkService.init(); if (pu.isOhos) Log.i('🟢 [OHOS] 深度链接服务初始化完成', null, null, LogCategory.router); } catch (e, st) { Log.e('深度链接服务初始化失败', e, st, LogCategory.router); } } try { await CrashMonitor.instance.init(); Log.i('崩溃监控服务初始化完成', null, null, LogCategory.service); } catch (e, st) { Log.e('崩溃监控服务初始化失败', e, st, LogCategory.service); } try { await WallpaperHealthService.init(); Log.i('壁纸源健康检测服务初始化完成', null, null, LogCategory.service); } catch (e, st) { Log.e('壁纸源健康检测服务初始化失败', e, st, LogCategory.service); } try { await SettingsChangeLogger.init(); Log.i('设置变更日志服务初始化完成', null, null, LogCategory.service); } catch (e, st) { Log.e('设置变更日志服务初始化失败', e, st, LogCategory.service); } try { await RssService.init(); Log.i('RSS服务初始化完成', null, null, LogCategory.network); } catch (e, st) { Log.e('RSS服务初始化失败', e, st, LogCategory.network); } try { await ExchangeRateService.init(); Log.i('汇率缓存服务初始化完成', null, null, LogCategory.network); } catch (e, st) { Log.e('汇率缓存服务初始化失败', e, st, LogCategory.network); } if (PostAgreementInitializer.shouldInit()) { Log.i('检测到老用户已完成引导,立即初始化权限敏感服务', null, null, LogCategory.general); // 通知原生端协议已同意(确保老用户升级后原生端也注册敏感插件) await _notifyNativeAgreementForExistingUsers(); try { await PostAgreementInitializer.init(); } catch (e, st) { Log.e('老用户权限敏感服务初始化失败', e, st, LogCategory.general); } } else { Log.i('新用户未同意协议,权限敏感服务延迟到协议同意后初始化', null, null, LogCategory.onboarding); } // 初始化 Catcher2 异常捕获并启动应用 // 使用 rootWidget 方式,Catcher2 内部调用 runApp,避免 Zone mismatch // 预先获取临时目录作为截图保存路径,避免 Catcher2 输出 "Screenshots path is empty" 警告 String catcherScreenshotsPath = ''; try { final tempDir = await getTemporaryDirectory(); catcherScreenshotsPath = '${tempDir.path}/catcher_screenshots'; } catch (e) { // 获取失败时保持空字符串,Catcher2 会输出警告但不影响功能 } if (_liquidGlassReady) { Catcher2ConfigService.instance.init( screenshotsPath: catcherScreenshotsPath, rootWidget: LiquidGlassWidgets.wrap( child: ProviderScope( overrides: [ authStateProvider.overrideWith((ref) => ref.watch(authProvider)), logoutProvider.overrideWith( (ref) => () => ref.read(authProvider.notifier).logout(), ), ], child: const XianyanApp(), ), ), ); } else { Catcher2ConfigService.instance.init( screenshotsPath: catcherScreenshotsPath, rootWidget: ProviderScope( overrides: [ authStateProvider.overrideWith((ref) => ref.watch(authProvider)), logoutProvider.overrideWith( (ref) => () => ref.read(authProvider.notifier).logout(), ), ], child: const XianyanApp(), ), ); } if (pu.isOhos) { Log.i('🟢 [OHOS] runApp() 已调用', null, null, LogCategory.general); } } void _validatePageRegistry() { final errors = PageRegistry.validateAll(); if (errors.isNotEmpty) { for (final e in errors) { Log.e('页面注册表验证失败: $e', null, null, LogCategory.general); } } else { Log.i( '页面注册表验证通过,共 ${PageRegistry.pageCount} 个页面已注册', null, null, LogCategory.general, ); } const route = AppRoutes.home; if (!PageRegistry.isRouteRegistered(route)) { Log.e('页面注册表验证: 首页路由 $route 未注册!', null, null, LogCategory.general); } } /// 通知原生端协议已同意(老用户升级后确保原生端注册敏感插件) Future _notifyNativeAgreementForExistingUsers() async { if (!Platform.isAndroid) return; try { const channel = MethodChannel('apps.xy.xianyan/agreement'); await channel.invokeMethod('markAgreementAccepted'); Log.i('老用户原生端协议通知成功', null, null, LogCategory.general); } catch (e) { Log.e('老用户原生端协议通知失败', e, null, LogCategory.general); } } /// 抑制 liquid_glass_widgets 内部 lgr 系列高频日志 /// lgr.geometry / lgr.render / lgr.render.layer 在 debug 模式下 /// 每帧输出大量 FINER 级别日志,严重影响控制台可读性 void _suppressLiquidGlassLogs() { // 启用层级日志控制,允许单独设置子 logger 的级别 hierarchicalLoggingEnabled = true; // 将 lgr 根 logger 级别设为 WARNING,FINER/FINE/INFO 级别日志不再输出 Logger('lgr').level = Level.WARNING; }