Files
xianyan/lib/main.dart
Developer 544f77c0ce chore: 完成v2.4.7版本迭代更新
本次更新包含多项功能优化与兼容性修复:
1. iOS/鸿蒙端添加加密出口合规配置,跳过App Store审核问卷
2. 新增学习计划设置页路由与国际化支持
3. 修复鸿蒙端剪贴板粘贴不工作问题,安装标准剪贴板拦截器
4. 优化收藏功能:兼容复合ID、添加状态同步与触觉反馈
5. 修复鸿蒙端相册保存兼容性,统一使用系统分享降级方案
6. 优化搜索快捷方式跳转逻辑,避免白屏问题
7. 更新本地化资源,新增闲情逸致、学习计划等模块翻译
8. 修复节气日期表排序与跨年边界问题
9. 优化设备信息页面显示,新增系统版本号展示
10. 重构文件传输二维码逻辑,使用纯URL提升兼容性
11. 优化设置项布局,避免文本溢出问题
12. 修复登录页记住账户功能,新增隐私协议守卫
13. 更新macOS依赖库,替换flutter_secure_storage为darwin版本
2026-06-17 08:45:34 +08:00

368 lines
12 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.
// ============================================================
// 闲言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';
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<void> _appMain() async {
if (pu.isDesktop) {
await windowManager.ensureInitialized();
const windowOptions = WindowOptions(
minimumSize: Size(400, 600),
title: '闲言',
center: true,
);
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<dynamic> errorAndStacktrace = pair as List<dynamic>;
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
if (_liquidGlassReady) {
Catcher2ConfigService.instance.init(
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(
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<void> _notifyNativeAgreementForExistingUsers() async {
if (!Platform.isAndroid) return;
try {
const channel = MethodChannel('apps.xy.xianyan/agreement');
await channel.invokeMethod<bool>('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 级别设为 WARNINGFINER/FINE/INFO 级别日志不再输出
Logger('lgr').level = Level.WARNING;
}