Android Dark Theme 深色模式實戰

支援深色模式 (Dark Mode) 是現代 App 的基本要求。良好的深色模式不僅是將背景變黑,更關乎視覺對比度、可讀性以及在低光源環境下的使用者舒適度。在 Jetpack Compose 中,透過 Material 3 的色彩系統,這一切都變得非常直觀。

核心機制:偵測與套用

Compose 透過 isSystemInDarkTheme() 偵測系統層級的設定,並在自定義的 Theme 中切換色彩配置。

@Composable
fun MyApplicationTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Android 12+ 支援動態色彩 (由使用者桌布衍生)
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }
        darkTheme -> DarkColorScheme
        else -> LightColorScheme
    }

    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        content = content
    )
}

語意化色彩 (Semantic Colors)

在開發時,絕對不要使用硬編碼顏色(如 Color.WhiteColor.Black)。你應該始終從 MaterialTheme.colorScheme 中選取具有「語意」的變數,這樣系統才能自動在兩種模式間切換。

常用的語意化色彩對應:

語意變數用途描述淺色模式表現深色模式表現
primary主要品牌顏色明亮主要色較淺/柔和的主色
onPrimary在主色上的文字/圖示白色或深色通常是極深色
surface卡片、選單、對話框背景白色/淺灰色深灰色
onSurface在 Surface 上的主文字深灰色/黑色白色/淺灰色
error錯誤訊息或提示紅色較亮的珊瑚紅
// ✅ 正確寫法:系統會根據主題自動切換顏色
Surface(
    color = MaterialTheme.colorScheme.background,
    contentColor = MaterialTheme.colorScheme.onBackground
) {
    Text("這是自適應文字")
}

// ❌ 錯誤寫法:在深色模式下會導致黑色背景配黑色文字(看不見)
Text("硬編碼文字", color = Color.Black)

圖示與圖片的處理

圖示染色 (Tinting)

大多數系統圖示應隨主題切換顏色。你可以直接存取 LocalContentColor 或語意顏色。

Icon(
    imageVector = Icons.Default.Favorite,
    contentDescription = null,
    tint = MaterialTheme.colorScheme.primary // 圖示會隨主題變色
)

圖片亮度過濾

有些圖片在全黑背景下可能顯得過於刺眼。你可以視情況在深色模式下降低圖片的透明度或亮度。

Image(
    painter = painterResource(id = R.drawable.my_image),
    contentDescription = null,
    alpha = if (isSystemInDarkTheme()) 0.8f else 1.0f // 深色模式下稍微調暗
)

測試與多重預覽 (Multi-Preview)

你可以透過多重 @Preview 註解,一次檢視頁面在不同模式下的呈現狀況。

@Preview(name = "淺色模式", showBackground = true)
@Preview(
    name = "深色模式",
    showBackground = true,
    uiMode = Configuration.UI_MODE_NIGHT_YES
)
@Composable
fun GreetingPreview() {
    MyApplicationTheme {
        Greeting("Android")
    }
}

總結

深色模式的核心在於「語意化」。只要在專案初期就養成使用 MaterialTheme.colorScheme 的習慣,你的 App 就能自動獲得高品質的深色主題支援。記住:深色模式應著重於降低眼睛疲勞,而非單純的顏色反轉。