Jetpack Compose 入门系列(九):Compose 中的主题与 Material 3

24 阅读12分钟

Jetpack Compose 入门系列(九):Compose 中的主题与 Material 3

学完上篇你已经知道如何用 LaunchedEffectDisposableEffect 等 API 处理副作用了。但一个 App 不只是能跑,还要看起来统一、专业,本篇解决一个问题:如何用 Material 3 管理颜色、字体、形状和深色模式


一、为什么需要主题

前面几篇里,我们已经写过不少界面:按钮、列表、详情页、注册页、课程卡片。你可能会发现一个问题:如果每个组件都自己写颜色和字号,代码很快会变乱。

比如:

Text(
    text = "课程标题",
    color = Color(0xFF1E88E5),
    fontSize = 22.sp
)

Button(
    colors = ButtonDefaults.buttonColors(
        containerColor = Color(0xFF1E88E5)
    ),
    onClick = {}
) {
    Text(text = "开始学习")
}

这段代码能跑,但问题是:

  1. 蓝色到处写,后面换品牌色很麻烦。
  2. 字号到处写,页面风格容易不统一。
  3. 深色模式要一个个改,改到怀疑人生。
  4. 组件越多,重复代码越多。

在 XML 时代,我们会把颜色写进 colors.xml,把样式写进 styles.xml

<color name="brand_primary">#1E88E5</color>

<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
    <item name="colorPrimary">@color/brand_primary</item>
</style>

到了 Compose 里,对应的核心就是 MaterialTheme

来做个对比:

XML 时代Compose 时代
colors.xmlColorScheme
styles.xmlMaterialTheme
TextAppearanceTypography
shapeAppearanceShapes
Theme.MaterialComponentsMaterialTheme

Material 3 的主题主要管三件事:

主题部分负责什么
colorScheme颜色,比如主色、背景色、错误色
typography字体样式,比如标题、正文、标签
shapes形状,比如按钮、卡片圆角

用一句话概括:主题就是把全 App 共用的视觉规则集中管理起来

flowchart TD
    A[MaterialTheme] --> B[colorScheme 颜色]
    A --> C[typography 字体]
    A --> D[shapes 形状]
    B --> E[Button / Card / TextField]
    C --> F[Text]
    D --> G[Card / Button / Surface]

关键点:不要在每个组件里到处写死颜色、字号和圆角。统一放进主题里,页面才会稳定、统一、好维护。


二、添加 Material 3 依赖

Material 3 是 Compose 里推荐使用的新一代 Material Design 组件库。

2.1 libs.versions.toml

[versions]
agp = "9.2.1"
kotlin = "2.2.10"
composeBom = "2026.02.01"
activity = "1.8.0"

[libraries]
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity" }
androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

2.2 app/build.gradle.kts

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.compose.compiler)
}

android {
    namespace = "com.example.materialtheme"
    compileSdk = 36

    defaultConfig {
        applicationId = "com.example.materialtheme"
        minSdk = 24
        targetSdk = 36
        versionCode = 1
        versionName = "1.0"
    }

    buildFeatures {
        compose = true
    }
}

dependencies {
    implementation(platform(libs.androidx.compose.bom))

    implementation(libs.androidx.activity.compose)
    implementation(libs.androidx.compose.material3)
    implementation(libs.androidx.compose.ui.tooling.preview)
}

这段配置里发生了什么:

  1. material3 提供 MaterialThemeButtonCardScaffoldSurface 等组件。
  2. Compose BOM 统一管理 Compose 相关依赖版本。
  3. ui-tooling-preview 用来写 @Preview 预览界面。

如果你用的是 Material 3,就优先从 androidx.compose.material3 包里导组件,不要和旧的 androidx.compose.material 混着用。


三、ColorScheme:统一管理颜色

Material 3 的颜色不再只是一两个 primaryaccent,而是一整套 ColorScheme

先定义一组颜色:

import androidx.compose.ui.graphics.Color

private val BrandBlue = Color(0xFF2563EB)
private val BrandBlueDark = Color(0xFF93C5FD)
private val BrandGreen = Color(0xFF16A34A)
private val BrandRed = Color(0xFFDC2626)
private val LightBackground = Color(0xFFF8FAFC)
private val DarkBackground = Color(0xFF0F172A)

然后定义浅色和深色主题:

import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme

private val LightColorScheme = lightColorScheme(
    primary = BrandBlue,
    onPrimary = Color.White,
    secondary = BrandGreen,
    onSecondary = Color.White,
    error = BrandRed,
    onError = Color.White,
    background = LightBackground,
    onBackground = Color(0xFF0F172A),
    surface = Color.White,
    onSurface = Color(0xFF0F172A)
)

private val DarkColorScheme = darkColorScheme(
    primary = BrandBlueDark,
    onPrimary = Color(0xFF172554),
    secondary = Color(0xFF86EFAC),
    onSecondary = Color(0xFF052E16),
    error = Color(0xFFFCA5A5),
    onError = Color(0xFF7F1D1D),
    background = DarkBackground,
    onBackground = Color(0xFFE2E8F0),
    surface = Color(0xFF1E293B),
    onSurface = Color(0xFFE2E8F0)
)

这段代码里发生了什么:

  1. primary 是主色,常用于主要按钮、重点信息。
  2. onPrimary 是显示在主色上的内容颜色,比如按钮文字。
  3. secondary 是辅助色,用于次要强调。
  4. background 是页面背景色。
  5. surface 是卡片、弹窗、导航栏这类表面的颜色。
  6. onSurface 是显示在 surface 上的文字颜色。

常用颜色速查:

颜色字段常见用途
primary主按钮、选中状态、重要操作
onPrimary主色上的文字或图标
secondary次要强调色
background页面背景
onBackground页面背景上的文字
surface卡片、底栏、弹窗背景
onSurfacesurface 上的文字
error错误提示、危险操作
onError错误色上的文字

使用时不要直接写死颜色:

❌ 错误:

Text(
    text = "课程标题",
    color = Color(0xFF2563EB)
)

✅ 正确:

Text(
    text = "课程标题",
    color = MaterialTheme.colorScheme.primary
)

这样以后换主题色,只需要改 ColorScheme,不用满项目搜索 0xFF2563EB

Material 3 颜色命名不是“这个颜色长什么样”,而是“这个颜色用在哪里”。比如 primary 不是固定蓝色,它表示品牌主色。

补充:Material 3 还支持 Android 12 及以上的动态颜色,可以根据系统壁纸生成颜色方案。本文为了让示例更稳定、品牌色更明确,先使用手动定义的 LightColorSchemeDarkColorScheme


四、Typography:统一管理字体样式

颜色统一之后,下一步就是字体。

在 XML 里,我们会用 TextAppearance 管理标题、正文、小字。

Compose 里用 Typography

import androidx.compose.material3.Typography
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp

private val DefaultTypography = Typography()

private val AppTypography = Typography(
    headlineLarge = DefaultTypography.headlineLarge.copy(
        fontSize = 30.sp,
        fontWeight = FontWeight.Bold,
        lineHeight = 38.sp
    ),
    headlineMedium = DefaultTypography.headlineMedium.copy(
        fontSize = 24.sp,
        fontWeight = FontWeight.Bold,
        lineHeight = 32.sp
    ),
    titleMedium = DefaultTypography.titleMedium.copy(
        fontSize = 18.sp,
        fontWeight = FontWeight.SemiBold,
        lineHeight = 26.sp
    ),
    bodyMedium = DefaultTypography.bodyMedium.copy(
        fontSize = 15.sp,
        lineHeight = 22.sp
    ),
    labelLarge = DefaultTypography.labelLarge.copy(
        fontSize = 14.sp,
        fontWeight = FontWeight.SemiBold
    )
)

使用时这样写:

Text(
    text = "课程商城",
    style = MaterialTheme.typography.headlineMedium
)

Text(
    text = "学习 Compose 主题和 Material 3 的基本用法。",
    style = MaterialTheme.typography.bodyMedium
)

这段代码里发生了什么:

  1. headlineMedium 用于页面标题。
  2. titleMedium 用于卡片标题。
  3. bodyMedium 用于普通正文。
  4. labelLarge 用于按钮文字、标签文字。
  5. 页面不再关心具体字号,只关心“这是标题还是正文”。

常用字体速查:

字体样式推荐用途
headlineLarge大标题、欢迎页标题
headlineMedium页面标题
titleMedium卡片标题、列表标题
bodyMedium正文说明
labelLarge按钮文字、标签

❌ 错误:

Text(
    text = "课程标题",
    fontSize = 18.sp,
    fontWeight = FontWeight.Bold
)

✅ 正确:

Text(
    text = "课程标题",
    style = MaterialTheme.typography.titleMedium
)

字体样式的重点不是“少写几行代码”,而是让整个 App 的文字层级统一。标题像标题,正文像正文,别每个页面都自由发挥。


五、Shapes:统一管理圆角

Material 3 里,形状主要通过 Shapes 管理。

比如按钮、卡片、输入框的圆角,不建议到处写死:

Card(
    shape = RoundedCornerShape(18.dp)
) {
    // ...
}

更推荐统一定义:

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp

private val AppShapes = Shapes(
    extraSmall = RoundedCornerShape(6.dp),
    small = RoundedCornerShape(10.dp),
    medium = RoundedCornerShape(16.dp),
    large = RoundedCornerShape(24.dp),
    extraLarge = RoundedCornerShape(32.dp)
)

使用:

Card(
    shape = MaterialTheme.shapes.medium
) {
    Text(
        text = "课程卡片",
        modifier = Modifier.padding(16.dp)
    )
}

常用形状速查:

形状推荐用途
extraSmall小标签、Chip
small小按钮、小输入区域
medium普通卡片、输入框
large大卡片、底部弹窗
extraLarge强视觉容器

圆角也是设计语言的一部分。一个页面 8dp、另一个页面 23dp、第三个页面 12dp,看起来就会很散。


六、封装 AppTheme:让主题真正生效

定义完颜色、字体、形状之后,需要把它们放进一个统一的 Theme 函数。

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable

@Composable
fun CourseAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme = if (darkTheme) {
        DarkColorScheme
    } else {
        LightColorScheme
    }

    MaterialTheme(
        colorScheme = colorScheme,
        typography = AppTypography,
        shapes = AppShapes,
        content = content
    )
}

然后在 MainActivity 中使用:

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CourseAppTheme {
                CourseHomeScreen()
            }
        }
    }
}

这段代码里发生了什么:

  1. isSystemInDarkTheme() 根据系统设置判断是否深色模式。
  2. darkTheme 为 true 时使用 DarkColorScheme
  3. darkTheme 为 false 时使用 LightColorScheme
  4. MaterialTheme 把颜色、字体、形状提供给内部所有 Composable。
  5. 内部组件可以通过 MaterialTheme.colorSchemeMaterialTheme.typographyMaterialTheme.shapes 获取主题值。

主题传递关系如下:

flowchart TD
    A[CourseAppTheme] --> B[MaterialTheme]
    B --> C[CourseHomeScreen]
    C --> D[CourseCard]
    C --> E[Button]
    C --> F[Text]
    D --> G[MaterialTheme.colorScheme.surface]
    E --> H[MaterialTheme.colorScheme.primary]
    F --> I[MaterialTheme.typography]

关键点:MaterialTheme 要包在 App 根部。这样内部页面和组件才能拿到统一主题。


七、常见错误:别把主题写散了

7.1 不要到处写死颜色

❌ 错误:

Button(
    colors = ButtonDefaults.buttonColors(
        containerColor = Color(0xFF2563EB)
    ),
    onClick = {}
) {
    Text(text = "开始")
}

✅ 正确:

Button(
    onClick = {}
) {
    Text(text = "开始")
}

Material 3 的 Button 默认就会使用 MaterialTheme.colorScheme.primary

Material 3 组件默认会从主题里读取合适的颜色,所以很多时候不用手动写 colors = ...。只有当组件语义和默认样式不一致时,才需要覆盖。

如果确实要自定义,也应该从主题里取:

Button(
    colors = ButtonDefaults.buttonColors(
        containerColor = MaterialTheme.colorScheme.secondary
    ),
    onClick = {}
) {
    Text(text = "次要操作")
}

能用组件默认主题就先用默认主题,只有明确需要区分时再覆盖颜色。

7.2 不要在每个 Text 里手写字号

❌ 错误:

Text(
    text = "页面标题",
    fontSize = 24.sp,
    fontWeight = FontWeight.Bold
)

✅ 正确:

Text(
    text = "页面标题",
    style = MaterialTheme.typography.headlineMedium
)

这样以后标题整体变大或变小,只改主题就行。

7.3 不要只做浅色,不管深色模式

❌ 错误:

MaterialTheme(
    colorScheme = LightColorScheme,
    content = content
)

✅ 正确:

val colorScheme = if (darkTheme) {
    DarkColorScheme
} else {
    LightColorScheme
}

MaterialTheme(
    colorScheme = colorScheme,
    content = content
)

深色模式不是最后再补的功能。主题一开始就把 Light 和 Dark 分开,后面会省很多事。


八、综合实战:课程首页主题 Demo

下面我们把前面的知识串起来,做一个课程首页:

  • 支持浅色 / 深色主题切换
  • 使用统一品牌色
  • 使用统一字体层级
  • 使用统一圆角
  • 卡片、按钮、标签都从 MaterialTheme 取值

完整代码如下:

package com.example.materialtheme

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Shapes
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.Typography
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CourseThemeDemoApp()
        }
    }
}

private val BrandBlue = Color(0xFF2563EB)
private val BrandBlueDark = Color(0xFF93C5FD)
private val BrandGreen = Color(0xFF16A34A)
private val BrandRed = Color(0xFFDC2626)
private val LightBackground = Color(0xFFF8FAFC)
private val DarkBackground = Color(0xFF0F172A)

private val LightColorScheme = lightColorScheme(
    primary = BrandBlue,
    onPrimary = Color.White,
    secondary = BrandGreen,
    onSecondary = Color.White,
    error = BrandRed,
    onError = Color.White,
    background = LightBackground,
    onBackground = Color(0xFF0F172A),
    surface = Color.White,
    onSurface = Color(0xFF0F172A)
)

private val DarkColorScheme = darkColorScheme(
    primary = BrandBlueDark,
    onPrimary = Color(0xFF172554),
    secondary = Color(0xFF86EFAC),
    onSecondary = Color(0xFF052E16),
    error = Color(0xFFFCA5A5),
    onError = Color(0xFF7F1D1D),
    background = DarkBackground,
    onBackground = Color(0xFFE2E8F0),
    surface = Color(0xFF1E293B),
    onSurface = Color(0xFFE2E8F0)
)

private val DefaultTypography = Typography()

private val AppTypography = Typography(
    headlineLarge = DefaultTypography.headlineLarge.copy(
        fontSize = 30.sp,
        fontWeight = FontWeight.Bold,
        lineHeight = 38.sp
    ),
    headlineMedium = DefaultTypography.headlineMedium.copy(
        fontSize = 24.sp,
        fontWeight = FontWeight.Bold,
        lineHeight = 32.sp
    ),
    titleMedium = DefaultTypography.titleMedium.copy(
        fontSize = 18.sp,
        fontWeight = FontWeight.SemiBold,
        lineHeight = 26.sp
    ),
    bodyMedium = DefaultTypography.bodyMedium.copy(
        fontSize = 15.sp,
        lineHeight = 22.sp
    ),
    labelLarge = DefaultTypography.labelLarge.copy(
        fontSize = 14.sp,
        fontWeight = FontWeight.SemiBold
    )
)

private val AppShapes = Shapes(
    extraSmall = RoundedCornerShape(6.dp),
    small = RoundedCornerShape(10.dp),
    medium = RoundedCornerShape(16.dp),
    large = RoundedCornerShape(24.dp),
    extraLarge = RoundedCornerShape(32.dp)
)

@Composable
private fun CourseThemeDemoApp() {
    val systemDarkTheme = isSystemInDarkTheme()
    var useDarkTheme by rememberSaveable {
        mutableStateOf(systemDarkTheme)
    }

    CourseAppTheme(
        darkTheme = useDarkTheme
    ) {
        CourseHomeScreen(
            useDarkTheme = useDarkTheme,
            onThemeChange = {
                useDarkTheme = it
            }
        )
    }
}

@Composable
private fun CourseAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme = if (darkTheme) {
        DarkColorScheme
    } else {
        LightColorScheme
    }

    MaterialTheme(
        colorScheme = colorScheme,
        typography = AppTypography,
        shapes = AppShapes,
        content = content
    )
}

@Composable
private fun CourseHomeScreen(
    useDarkTheme: Boolean,
    onThemeChange: (Boolean) -> Unit
) {
    Scaffold(
        containerColor = MaterialTheme.colorScheme.background
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(innerPadding)
                .padding(24.dp),
            verticalArrangement = Arrangement.spacedBy(16.dp)
        ) {
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween
            ) {
                Column {
                    Text(
                        text = "课程商城",
                        style = MaterialTheme.typography.headlineMedium,
                        color = MaterialTheme.colorScheme.onBackground
                    )

                    Text(
                        text = "统一主题,让界面更像一个产品",
                        style = MaterialTheme.typography.bodyMedium,
                        color = MaterialTheme.colorScheme.onBackground
                    )
                }

                Switch(
                    checked = useDarkTheme,
                    onCheckedChange = onThemeChange
                )
            }

            CourseSummaryCard()

            CourseCard(
                title = "Compose 主题与 Material 3",
                description = "学习颜色、字体、形状和深色模式的统一管理。",
                tag = "主题"
            )

            CourseCard(
                title = "Material 3 组件实战",
                description = "用 Button、Card、Surface 组合出统一风格界面。",
                tag = "组件"
            )

            Row(
                horizontalArrangement = Arrangement.spacedBy(12.dp)
            ) {
                Button(
                    onClick = {}
                ) {
                    Text(text = "开始学习")
                }

                OutlinedButton(
                    onClick = {}
                ) {
                    Text(text = "查看大纲")
                }
            }
        }
    }
}

@Composable
private fun CourseSummaryCard() {
    Surface(
        modifier = Modifier.fillMaxWidth(),
        color = MaterialTheme.colorScheme.primary,
        contentColor = MaterialTheme.colorScheme.onPrimary,
        shape = MaterialTheme.shapes.large
    ) {
        Column(
            modifier = Modifier.padding(20.dp),
            verticalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            Text(
                text = "本周推荐",
                style = MaterialTheme.typography.labelLarge
            )

            Text(
                text = "把主题系统搭好,后面写页面会轻松很多。",
                style = MaterialTheme.typography.bodyMedium
            )
        }
    }
}

@Composable
private fun CourseCard(
    title: String,
    description: String,
    tag: String
) {
    Card(
        modifier = Modifier.fillMaxWidth(),
        shape = MaterialTheme.shapes.medium,
        colors = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.surface,
            contentColor = MaterialTheme.colorScheme.onSurface
        ),
        elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
    ) {
        Column(
            modifier = Modifier.padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(10.dp)
        ) {
            Text(
                text = title,
                style = MaterialTheme.typography.titleMedium
            )

            Text(
                text = description,
                style = MaterialTheme.typography.bodyMedium
            )

            Text(
                text = tag,
                modifier = Modifier
                    .background(
                        color = MaterialTheme.colorScheme.secondary,
                        shape = MaterialTheme.shapes.extraSmall
                    )
                    .padding(horizontal = 10.dp, vertical = 4.dp),
                color = MaterialTheme.colorScheme.onSecondary,
                style = MaterialTheme.typography.labelLarge
            )
        }
    }
}

这里的 useDarkTheme 初始值来自 isSystemInDarkTheme(),所以第一次进入页面时会跟随系统深色模式;用户点击 Switch 后,再使用用户手动选择的主题。

真实项目中,用户选择的主题通常会保存到 DataStore,下次启动时继续使用。这里为了聚焦主题本身,先用 rememberSaveable 保存页面内状态。

这份 Demo 中,主题数据流是这样的:

flowchart TD
    A[Switch 切换主题] --> B[useDarkTheme 状态变化]
    B --> C[CourseAppTheme 选择 ColorScheme]
    C --> D[MaterialTheme 提供主题]
    D --> E[CourseHomeScreen]
    E --> F[CourseSummaryCard]
    E --> G[CourseCard]
    F --> H[读取 colorScheme / typography / shapes]
    G --> I[读取 colorScheme / typography / shapes]

实战知识点对应表

实战中的效果使用的 API对应章节
品牌色统一管理lightColorScheme / darkColorScheme
标题和正文统一样式Typography
卡片和标签统一圆角Shapes
全局主题封装CourseAppTheme
深色模式切换darkTheme + MaterialTheme六、八
组件从主题取值MaterialTheme.colorScheme三、八

这就是主题系统的价值:页面组件不用知道具体颜色、字号、圆角是多少,只需要知道自己在用哪种语义角色。


九、Material 3 主题速查表

你想做什么推荐写法
设置主色lightColorScheme(primary = ...)
设置深色主题darkColorScheme(...)
获取主色MaterialTheme.colorScheme.primary
获取页面背景色MaterialTheme.colorScheme.background
获取卡片色MaterialTheme.colorScheme.surface
设置页面标题样式MaterialTheme.typography.headlineMedium
设置卡片标题样式MaterialTheme.typography.titleMedium
设置正文样式MaterialTheme.typography.bodyMedium
设置卡片圆角MaterialTheme.shapes.medium
封装全局主题MaterialTheme(colorScheme, typography, shapes)

十、总结

本篇你学到了 Compose 中主题与 Material 3 的核心用法:

  1. 主题的意义:把颜色、字体、形状这些全局视觉规则集中管理,避免写散
  2. ColorScheme:用语义化颜色管理主色、背景色、卡片色、错误色
  3. 深色模式:用 lightColorSchemedarkColorScheme 分别定义浅色与深色主题
  4. Typography:统一标题、正文、标签等字体层级
  5. Shapes:统一按钮、卡片、标签等组件圆角
  6. MaterialTheme:在 App 根部提供主题,让内部组件统一读取
  7. 组件默认主题:Material 3 组件默认会读取主题,不需要到处手写颜色

核心原则:不要让每个页面自己决定颜色、字号和圆角。主题先统一,组件再复用,App 才会像一个整体

下一篇我们将学习 Paging 3 分页加载——如何在真实列表场景中处理分页加载、加载更多、刷新、空状态和错误重试。


如果你在学习过程中有任何疑问,欢迎在评论区留言,我会尽可能回复。

系列文章: