You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

KMP桌面端如何响应系统主题变更动态切换配色方案?

解决Compose Multiplatform桌面端系统主题变更不自动响应问题

问题分析

Android和iOS端的isSystemInDarkTheme()会自动监听系统主题变化并触发Compose重组,但桌面端(Windows/macOS/Linux)的该API仅在应用启动时读取一次系统主题状态,后续系统主题变更不会自动更新应用内的配色方案。你添加的-Dapple.awt.application.appearance=system仅控制窗口标题栏的主题,无法触发应用内容的主题切换。

解决方案

需要手动实现桌面端的系统主题监听,将主题状态转为可观察的Compose State,从而在主题变化时触发重组。

步骤1:定义平台相关的主题监听Flow

创建跨平台的expect/actual函数,分别为桌面端实现主题变化监听:

// commonMain 下的通用定义
import kotlinx.coroutines.flow.Flow

expect fun getSystemDarkThemeFlow(): Flow<Boolean>
// desktopMain 下的实现
import java.beans.PropertyChangeListener
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import androidx.compose.foundation.isSystemInDarkTheme

actual fun getSystemDarkThemeFlow(): Flow<Boolean> {
    return flow {
        // 发送初始主题状态
        emit(isSystemInDarkTheme())
        
        val osName = System.getProperty("os.name")
        when {
            osName.contains("Mac", ignoreCase = true) -> {
                // macOS 监听Apple属性变化
                val application = Class.forName("com.apple.eawt.Application")
                    .getMethod("getApplication").invoke(null)
                application.javaClass.getMethod(
                    "addPropertyChangeListener",
                    PropertyChangeListener::class.java
                ).invoke(application, PropertyChangeListener { evt ->
                    if (evt.propertyName == "apple.awt.application.appearance") {
                        emit(isSystemInDarkTheme())
                    }
                })
            }
            osName.contains("Windows", ignoreCase = true) -> {
                // Windows 定时检查主题变化(可优化为JNA监听系统消息)
                while (true) {
                    delay(1000)
                    val currentTheme = isSystemInDarkTheme()
                    emit(currentTheme)
                }
            }
            else -> {
                // Linux 定时检查主题变化
                while (true) {
                    delay(1000)
                    val currentTheme = isSystemInDarkTheme()
                    emit(currentTheme)
                }
            }
        }
    }.distinctUntilChanged() // 避免重复发送相同状态
}

步骤2:在Compose中使用可观察的主题状态

创建一个remember函数,将Flow转为Compose State,确保主题变化时触发重组:

// commonMain 下的Composable函数
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.State

@Composable
fun rememberSystemDarkThemeState(): State<Boolean> {
    val themeFlow = remember { getSystemDarkThemeFlow() }
    return themeFlow.collectAsState(initial = isSystemInDarkTheme())
}

步骤3:修改App Composable使用新的状态

替换原来的isSystemInDarkTheme()调用,改为使用rememberSystemDarkThemeState():

@Composable
fun App() {
    val isDarkTheme = rememberSystemDarkThemeState().value

    MaterialTheme(
        colorScheme = if(isDarkTheme) darkColorScheme() else lightColorScheme(),
        typography = sampleTypography(),
    ) {
      AppContent()
    }
}

补充说明

  • macOS的监听方式是利用Apple的AWT属性变化通知,能实时响应主题切换;
  • Windows的定时检查是简易实现,若需要更高效的监听,可以使用JNA库监听系统的WM_SETTINGCHANGE消息;
  • 保留原有的-Dapple.awt.application.appearance=system配置,确保窗口标题栏与系统主题同步;
  • Android和iOS端无需额外修改,isSystemInDarkTheme()会自动处理主题变化。

内容的提问来源于stack exchange,提问作者Pablo Bayón

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 03:06:01