Android Material Design 3 主題與樣式系統
Material Design 3 (M3) 是 Google 最新的設計語言,也被稱為 Material You。它在 Android 12 (API 31) 之後變得尤為重要,核心理念是「個人化」與「動態性」。Jetpack Compose 原生支援 M3,透過 MaterialTheme Composable 來統一管理。
主題的三大支柱
一個定義良好的 M3 主題由以下三個 Composable 參數組成:
MaterialTheme(
colorScheme = MyColorScheme, // 顏色方案
typography = MyTypography, // 排版系統
shapes = MyShapes, // 形狀定義
content = content
)
詳解 Color Scheme (顏色角色)
M3 捨棄了過去簡單的 Primary/Secondary 概念,導入了更詳盡的「角色 (Roles)」。
1. 核心角色
- Primary:App 最主要的品牌色。
- Secondary:輔助色,用於較不突出的元件。
- Tertiary:第三色,用於強調或與品牌色形成對比的裝飾。
2. 「On」顏色與容器 (Containers)
這是 M3 最重要的概念。每種顏色都有對應的 on 顏色和 Container 變體:
- PrimaryContainer:比 Primary 更淡,適合做為按鈕或卡片的背景。
- OnPrimary:放在 Primary 顏色之上的文字或圖示色(確保對比度正確)。
- OnPrimaryContainer:放在 PrimaryContainer 之上的文字色。
val LightColors = lightColorScheme(
primary = Color(0xFF6750A4),
onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFFEADDFF),
onPrimaryContainer = Color(0xFF21005D),
// ... 設定其他角色
)
3. 動態色彩 (Dynamic Color)
如果裝置支援 Android 12+,你可以直接從使用者的桌布取色,讓你的 App 看起來與系統環境融為一體。
@Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true, // 預設開啟動態色彩
content: @Composable () -> Unit
) {
val colorScheme = when {
// Android 12+ 且支援動態色彩
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColors
else -> LightColors
}
MaterialTheme(colorScheme = colorScheme, content = content)
}
排版系統 (Typography)
M3 定義了 15 種標準樣式,涵蓋了從巨大的顯示文字到微小的標籤:
- Display (Large/Medium/Small):最重要的宣傳文字。
- Headline (Large/Medium/Small):分區的大標題。
- Title (Large/Medium/Small):導航列或中等標題。
- Body (Large/Medium/Small):正文內容。
- Label (Large/Medium/Small):按鈕文字或小型說明。
自訂字體
你可以整合 Google Fonts 或本地字體檔。
val provider = GoogleFont.Provider(...)
val fontName = GoogleFont("Inter")
val fontFamily = FontFamily(Font(googleFont = fontName, fontProvider = provider))
val MyTypography = Typography(
bodyLarge = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
),
titleLarge = TextStyle(...)
)
形狀 (Shapes) 與圓角
M3 將組件分為五類形狀需求:
- Extra Small:小圖示或迷你元件 (預設 4.dp)。
- Small:提示視窗 (預設 8.dp)。
- Medium:卡片或選單 (預設 12.dp)。
- Large:對話框或大容器 (預設 28.dp)。
- Extra Large:全螢幕背景或最大的圓角塊 (預設 28.dp)。
val MyShapes = Shapes(
small = RoundedCornerShape(8.dp),
medium = RoundedCornerShape(12.dp),
large = RoundedCornerShape(16.dp)
)
// 使用方式
Card(shape = MaterialTheme.shapes.medium) { ... }
Tonal Elevation (色調高度)
在 M3 中,立體感不再只靠陰影。Elevation 現在會影響顏色。 這意味著元件的高度越高,它的底層色調就會混合越多 Primary 色調,顏色變得越深(或在淺色模式下變得更飽和)。
Surface(
tonalElevation = 4.dp, // 不產生陰影,而是讓背景顏色變深
shadowElevation = 0.dp
) {
// 內容
}
實戰範例 (Practical Examples)
1. 自訂樣式的按鈕
結合 Secondary 顏色與 Large 形狀(膠囊形狀)。
Button(
onClick = { /* TODO */ },
shape = MaterialTheme.shapes.large, // 使用主題定義的 Large 圓角
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondary,
contentColor = MaterialTheme.colorScheme.onSecondary
)
) {
Text("了解更多")
}
2. 精美的個人資料卡片
綜合運用排版、顏色與色調高度。
@Composable
fun ProfileCard(userName: String, bio: String) {
Surface(
modifier = Modifier.padding(16.dp),
shape = MaterialTheme.shapes.medium,
tonalElevation = 2.dp // 使用色調高度增加層次感
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = userName,
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = bio,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
3. 手動切換深色模式
在實務中,我們常需要讓使用者在設定中手動切換主題。
// 自定義主題 Composable
@Composable
fun AppTheme(
isDarkMode: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val myColors = if (isDarkMode) DarkColors else LightColors
MaterialTheme(
colorScheme = myColors,
typography = MyTypography,
shapes = MyShapes,
content = content
)
}
// 在 UI 中切換
var isUserDarkMode by remember { mutableStateOf(false) }
AppTheme(isDarkMode = isUserDarkMode) {
Switch(
checked = isUserDarkMode,
onCheckedChange = { isUserDarkMode = it }
)
}
主題開發最佳實踐
- 絕對不要硬編碼 (Hard-coding):不要直接寫
Color.White或fontSize = 18.sp。這樣會破壞深色模式的支援。總是使用MaterialTheme.colorScheme.surface。 - 語意化優先:如果你需要一個醒目的顏色,使用
primary;如果你需要一個警示,使用error。 - 處理對比度:總是成對使用顏色。例如用
MaterialTheme.colorScheme.primary當背景時,文字必須用MaterialTheme.colorScheme.onPrimary。 - 工具推薦:建議使用 Material Theme Builder 網頁工具來產生配色程式碼,這能確保你的配色邏輯符合 M3 的色調調色盤規則。
如果你是從 Material Design 2 遷移過來,請注意許多 Composable (如
Scaffold, TopAppBar) 現在都位於 androidx.compose.material3 套件下,千萬不要混用 M2 和 M3 的組件,這會導致主題抓取錯誤。