Files
xianyan/lib/app/services/theme_sync_service.dart
Developer 93d580fa1e feat(macos): 实现 macOS 系统菜单栏多语言支持与闪退修复
本次提交完成 macOS 端多项关键优化与修复:
1. 修复启动期竞态闪退问题,通过前移窗口属性初始化、串行化特效调用、跳过首次同步实现稳定启动
2. 实现系统菜单栏多语言本地化,支持中英日韩繁五种语言,软件内切换语言可同步更新菜单栏
3. 移除视图菜单中重复的全屏按钮,统一窗口标题栏逻辑
4. 新增 macOS App Store 打包配置与本地化资源
2026-06-24 08:35:08 +08:00

134 lines
4.7 KiB
Dart
Raw Permalink 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-06-22
/// 更新时间: 2026-06-22
/// 作用: 将主题状态同步到桌面端平台macOS/Windows管理窗口特效和托盘图标主题
/// 上次更新: 从 app.dart 中提取主题同步职责,实现单一职责分离
/// ============================================================
import 'package:flutter/widgets.dart';
import '../../core/services/device/macos_platform_service.dart';
import '../../core/services/device/windows_platform_service.dart';
import '../../core/services/desktop/desktop_window_effect_service.dart';
import '../../core/utils/logger.dart';
import '../../core/utils/platform/platform_utils.dart' as pu;
import '../../features/desktop/desktop_tray_controller.dart';
/// 主题同步服务
///
/// 负责将应用主题状态同步到桌面端原生平台:
/// - macOS/Windows 原生主题同步
/// - 窗口特效(毛玻璃/Acrylic/Mica主题同步
/// - 托盘图标主题同步
///
/// 使用去重机制避免每次 build 重复调用平台 API。
class ThemeSyncService {
/// 上次同步到桌面端的暗色状态(去重用)
bool? _lastDesktopDarkState;
/// 上次同步到桌面端的纯黑模式状态(去重用)
///
/// 单独跟踪 amoled 状态,因为 dark 和 amoled 模式的 [effectiveIsDark] 均为 true
/// 仅靠 [_lastDesktopDarkState] 无法检测 dark↔amoled 切换。
bool? _lastDesktopAmoledState;
/// 是否已跳过首次窗口特效同步
///
/// 首次窗口特效由 [DesktopServicesManager._initWindowEffect] 统一处理
/// (含 500ms 延迟等待 Impeller 完成初始渲染)。
/// 此处跳过首次同步,避免 build() 与 PostFrame 回调并发触发 applyEffect
/// 导致 NSWindow 属性修改与 Impeller raster 线程竞态崩溃。
bool _windowEffectFirstSyncSkipped = false;
/// 同步主题到桌面端原生平台
///
/// 仅在状态变化时同步,避免重复调用。
/// [isDark] 当前是否暗色模式
/// [isAmoled] 当前是否纯黑模式
/// [trayController] 托盘控制器,用于同步托盘图标主题
void syncThemeToDesktop({
required bool isDark,
required bool isAmoled,
DesktopTrayController? trayController,
}) {
// 同步原生平台主题macOS/Windows
if (_lastDesktopDarkState != isDark) {
_lastDesktopDarkState = isDark;
if (pu.isMacOS) {
MacosPlatformService.syncTheme(isDark);
}
if (pu.isWindows) {
WindowsPlatformService.syncTheme(isDark);
}
}
// 同步窗口特效和托盘图标(需要同时检测 dark 和 amoled 变化)
if (_lastDesktopDarkState != isDark ||
_lastDesktopAmoledState != isAmoled) {
_lastDesktopDarkState = isDark;
_lastDesktopAmoledState = isAmoled;
// 跳过首次窗口特效同步:由 DesktopServicesManager._initWindowEffect 统一处理
// (含 500ms 延迟等待 Impeller 完成初始渲染),避免启动期并发调用导致竞态崩溃
if (!_windowEffectFirstSyncSkipped) {
_windowEffectFirstSyncSkipped = true;
Log.i('跳过首次窗口特效同步,由 _initWindowEffect 统一处理');
} else {
// 同步窗口特效
DesktopWindowEffectService.instance
.applyEffect(isDark: isDark)
.catchError((Object e) {
Log.e('窗口特效主题同步失败', e);
});
}
// 同步托盘图标主题
if (trayController != null) {
// 使用 addPostFrameCallback 确保 Widget 树已构建
WidgetsBinding.instance.addPostFrameCallback((_) {
trayController.onThemeChanged(isDark, isAmoled: isAmoled);
});
}
}
}
/// 系统亮度变化时同步主题
///
/// 仅在跟随系统模式下调用。
/// [isDark] 系统当前是否暗色模式
/// [trayController] 托盘控制器
void syncFromSystemBrightness({
required bool isDark,
DesktopTrayController? trayController,
}) {
// 跟随系统模式下 isAmoled 始终为 false
_lastDesktopDarkState = isDark;
_lastDesktopAmoledState = false;
if (pu.isMacOS) {
MacosPlatformService.syncTheme(isDark);
}
if (pu.isWindows) {
WindowsPlatformService.syncTheme(isDark);
}
// 同步窗口特效
DesktopWindowEffectService.instance
.applyEffect(isDark: isDark)
.catchError((Object e) {
Log.e('窗口特效主题同步失败(系统亮度变化)', e);
});
// 同步托盘图标主题
trayController?.onThemeChanged(isDark);
}
/// 重置状态(用于测试)
void reset() {
_lastDesktopDarkState = null;
_lastDesktopAmoledState = null;
_windowEffectFirstSyncSkipped = false;
}
}