👋 哈喽大家好,欢迎回到Compose零基础系列~ 前面五篇我们已经循序渐进,把基础组件、三大布局、Modifier样式控制、状态管理,还有列表组件LazyColumn/LazyRow全都吃透了,现在不管是静态UI、简单交互,还是列表展示,都能轻松上手写出来。
但不知道大家在实操的时候有没有发现一个很麻烦的问题:我们写的UI,所有的颜色、字体、圆角,全都是“写死”的。比如按钮颜色直接写Color(0xFF6200EE),文本字体大小写16.sp,卡片圆角写12.dp,当时写的时候觉得很方便,可一旦遇到需要调整的情况,麻烦就来了。
比如产品突然说:“这个主色不好看,换成蓝色吧”“所有卡片的圆角统一改成16.dp”“给APP加个深色模式,晚上用着不刺眼”,这时候你就得全局搜索,一个个找到写死的颜色、圆角、字体,逐行修改,稍微不注意就会漏改,改到崩溃不说,还容易出错。
这一篇,我们就来彻底解决这个问题——学习Material3主题系统。Material3是Google推荐的Compose官方设计系统,核心就是帮我们“统一管理”APP的视觉样式,学会它之后,你整个APP的颜色、字体、形状,只需要改一处,全局就能自动生效,还能轻松实现浅色/深色模式切换,代码规范度、可维护性直接拉满,再也不用为修改样式头疼。
本篇全程大白话,不堆砌专业术语,不搞生硬的官方话术,就像我自己学完之后总结的实战笔记,每个知识点都搭配详细的代码示例,还有真实开发中的注意事项,新手能直接复制代码运行,一步步跟着操作就能学会,完全不用怕看不懂。
本篇核心目标:搞懂Material3主题的核心作用,明白为什么要使用主题;学会使用Android Studio默认生成的Material3主题,知道主题的基本结构;掌握自定义主题的核心步骤,能自定义颜色、字体、形状(圆角);学会实现浅色/深色模式的自动切换和手动切换;掌握主题的实际应用技巧,能在项目中灵活运用,让APP风格统一、好维护、易扩展;避开新手写主题时的常见坑,养成规范的开发习惯。
🔗 前文回顾:上一篇我们重点学了LazyColumn和LazyRow,搞定了各种列表展示场景,这一篇我们就来优化UI的“颜值”和“可维护性”,用主题统一管理所有样式,建议按系列顺序学,前后衔接更顺,理解起来也更轻松哦!
一、先说人话:Material3主题到底有啥用?(新手必看)
很多新手可能会觉得,“主题”听起来很复杂,没必要学,反正写死样式也能实现效果。但我敢说,只要你写的不是一次性的demo,而是真正的项目,主题一定会帮你节省大量时间,避免很多麻烦。
我们先对比两种开发方式,大家就明白主题的重要性了:
1.1 没有主题(写死样式)
这是我们前几篇写demo时的方式,也是新手最容易养成的习惯,比如:
// 按钮:写死颜色、圆角
Button(
onClick = {},
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF6200EE)),
shape = RoundedCornerShape(12.dp)
) {
// 文本:写死字体大小、颜色
Text(text = "点击按钮", fontSize = 16.sp, color = Color.White)
}
// 卡片:写死背景、圆角、阴影
Box(
modifier = Modifier
.background(Color.White, RoundedCornerShape(12.dp))
.shadow(4.dp, RoundedCornerShape(12.dp))
) {
Text(text = "卡片内容", fontSize = 14.sp, color = Color(0xFF1C1B1F))
}
这种方式的问题很明显:
- 维护成本极高:一旦需要修改主色、圆角、字体,就得全局搜索,逐行修改,漏改一个地方就会出现样式混乱。
- 样式不统一:多个页面、多个组件,很容易出现“圆角有的12.dp、有的8.dp”“字体有的16.sp、有的15.sp”,整个APP看起来很杂乱。
- 无法支持深色模式:如果要加深色模式,就得给每个组件单独判断“当前是浅色还是深色”,写大量重复代码,极其繁琐。
1.2 有主题(统一管理)
用Material3主题之后,我们的写法会变成这样,大家可以对比一下:
// 按钮:引用主题中的颜色、圆角,不写死
Button(
onClick = {},
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary),
shape = MaterialTheme.shapes.medium
) {
// 文本:引用主题中的字体样式、文字颜色
Text(
text = "点击按钮",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onPrimary
)
}
// 卡片:引用主题中的背景、圆角、阴影
Box(
modifier = Modifier
.background(MaterialTheme.colorScheme.surface, MaterialTheme.shapes.medium)
.shadow(4.dp, MaterialTheme.shapes.medium)
) {
Text(
text = "卡片内容",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface
)
}
这种方式的优势一目了然:
- 维护成本极低:想换主色,只需要修改主题中“primary”的颜色值,所有引用这个颜色的组件都会自动同步,不用改任何页面。
- 样式统一:所有组件的颜色、字体、圆角,都来自主题的统一定义,整个APP风格一致,看起来更专业。
- 轻松支持深色模式:主题本身就区分浅色和深色两套配置,切换模式时,所有组件会自动适配对应的样式,不用写额外代码。
简单总结一句话:Material3主题,就是APP视觉样式的“总开关”,统一定义、全局引用,让样式管理更简单、更规范。不管是小项目还是大项目,只要涉及到UI样式,主题都是必不可少的。
二、基础入门:使用系统默认的Material3主题
其实我们从第一篇开始,就已经在使用主题了——Android Studio创建Compose项目时,会自动帮我们生成一个默认的Material3主题,只是我们之前没有深入了解它的结构和用法。
我们先找到项目中的主题文件,一般在“ui/theme”目录下,有三个文件:Color.kt、Type.kt、Shape.kt,还有一个Theme.kt,这四个文件共同组成了Material3主题,我们逐一拆解,搞懂每个文件的作用。
2.1 主题的核心结构(3个核心部分)
Material3主题主要由三个部分组成,对应三个文件,分工明确,新手一定要记清楚:
- Color(颜色) :对应Color.kt,定义APP的所有颜色,比如主色、次要色、背景色、文字色等,分为浅色和深色两套配置。
- Typography(字体) :对应Type.kt,定义APP的所有字体样式,比如标题、正文、小字体的大小、粗细、行高等。
- Shape(形状) :对应Shape.kt,定义APP中所有组件的形状,主要是圆角大小,比如小控件、卡片、按钮的圆角。
而Theme.kt中的MyAppTheme函数,就是把这三个部分整合起来,然后包裹我们的整个APP,让所有组件都能引用到主题中的样式。
2.2 默认主题的代码解析(可直接复用)
我们先看默认生成的代码,不用死记硬背,理解每个部分的作用即可,后续我们再自定义修改。
第一步:Color.kt(颜色定义)
// 浅色模式颜色配置
val lightColorScheme = lightColorScheme(
primary = Color(0xFF6200EE), // 主色:按钮、标题、重点文字等
onPrimary = Color.White, // 主色上的文字/图标颜色(保证对比度,比如白色文字在紫色主色上)
primaryContainer = Color(0xFFE3D2FF), // 主色容器颜色(比如卡片背景)
onPrimaryContainer = Color(0xFF21005D),// 主色容器上的文字颜色
secondary = Color(0xFF7C4DFF), // 次要色:辅助元素、次要按钮等
onSecondary = Color.White, // 次要色上的文字颜色
background = Color(0xFFFFFBFE), // 页面背景色
onBackground = Color(0xFF1C1B1F), // 背景上的文字颜色
surface = Color(0xFFFFFBFE), // 表面色:卡片、按钮、输入框等组件的背景
onSurface = Color(0xFF1C1B1F), // 表面上的文字颜色
error = Color(0xFFB3261E), // 错误色:输入错误、提示错误等
onError = Color.White // 错误色上的文字颜色
)
// 深色模式颜色配置
val darkColorScheme = darkColorScheme(
primary = Color(0xFFBB86FC),
onPrimary = Color(0xFF3700B3),
primaryContainer = Color(0xFF4F378B),
onPrimaryContainer = Color(0xFFE9D8FD),
secondary = Color(0xFFD1C4FE),
onSecondary = Color(0xFF371E73),
background = Color(0xFF121212),
onBackground = Color(0xFFE1E1E1),
surface = Color(0xFF121212),
onSurface = Color(0xFFE1E1E1),
error = Color(0xFFF2B8B5),
onError = Color(0xFF601410)
)
重点说明:每个颜色都有对应的“onXXX”颜色,比如primary对应onPrimary,这是Material3的设计规范,目的是保证文字和背景的对比度,让用户能清晰看到内容,比如主色是深色,onPrimary就用浅色,反之亦然,新手不用自己瞎配,跟着这个规范来就行。
第二步:Type.kt(字体定义)
// 字体样式配置
val Typography = Typography(
displayLarge = TextStyle(
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
lineHeight = 40.sp,
letterSpacing = 0.sp
),
displayMedium = TextStyle(
fontSize = 28.sp,
fontWeight = FontWeight.Bold,
lineHeight = 36.sp,
letterSpacing = 0.sp
),
headlineSmall = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
titleLarge = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
lineHeight = 24.sp,
letterSpacing = 0.sp
),
bodyLarge = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
),
bodyMedium = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
lineHeight = 20.sp,
letterSpacing = 0.25.sp
),
labelLarge = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
lineHeight = 20.sp,
letterSpacing = 0.1.sp
)
)
重点说明:Material3定义了多种字体样式,对应不同的使用场景,新手不用全部记住,记住常用的几个即可:
- headlineSmall:页面标题(比如页面顶部的标题)
- titleLarge:卡片标题、按钮文字
- bodyLarge:正文文字(最常用)
- bodyMedium:次要正文、提示文字
第三步:Shape.kt(形状定义)
// 形状(圆角)配置
val Shapes = Shapes(
small = RoundedCornerShape(4.dp), // 小控件:比如小按钮、图标
medium = RoundedCornerShape(8.dp), // 中等控件:卡片、按钮、输入框(最常用)
large = RoundedCornerShape(16.dp) // 大控件:大卡片、弹窗
)
重点说明:shape主要控制组件的圆角,默认是8.dp、16.dp等,后续我们可以根据自己的需求修改,比如把所有卡片的圆角统一改成12.dp,只需要改这里的medium值即可。
第四步:Theme.kt(主题整合)
// 主题整合函数,包裹整个APP
@Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(), // 是否开启深色模式(默认跟随系统)
// 动态颜色(Android 12+支持,可选,新手可以先不用管)
dynamicColor: Boolean = true,
content: @Composable () -> Unit // 接收我们的APP内容
) {
// 根据是否开启深色模式,选择对应的颜色配置
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,
shapes = Shapes,
content = content
)
}
重点说明:MyAppTheme是一个Composable函数,我们只需要把整个APP的内容(所有页面)放在这个函数的content参数里,所有组件就能自动引用到主题中的样式。
2.3 如何使用默认主题?(实战演示)
使用主题非常简单,只需要在MainActivity中,用MyAppTheme包裹我们的页面即可,比如:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// 用主题包裹整个APP内容
MyAppTheme {
// 这里放我们的页面,所有页面都能引用主题样式
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MainScreen() // 我们自己写的主页面
}
}
}
}
}
然后在我们的页面组件中,直接引用主题中的样式,比如写一个按钮、一个卡片:
@Composable
fun MainScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// 按钮:引用主题的主色、圆角、字体
Button(
onClick = {},
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary),
shape = MaterialTheme.shapes.medium
) {
Text(
text = "主题按钮",
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.onPrimary
)
}
// 卡片:引用主题的表面色、圆角、阴影
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp)
.background(MaterialTheme.colorScheme.surface, MaterialTheme.shapes.medium)
.shadow(4.dp, MaterialTheme.shapes.medium)
.padding(16.dp)
) {
Text(
text = "主题卡片",
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurface
)
}
}
}
✅ 效果说明:运行后,按钮会显示主题默认的紫色(primary),卡片会显示默认的白色(surface),圆角是8.dp,字体样式也和主题定义的一致。如果我们切换系统的浅色/深色模式,按钮和卡片的颜色会自动切换,不用改任何代码。
三、核心实战:自定义Material3主题(最常用)
默认主题的颜色、字体、圆角,不一定符合我们的项目需求,比如默认主色是紫色,我们想改成蓝色;默认圆角是8.dp,我们想改成12.dp,这时候就需要自定义主题。
自定义主题非常简单,只需要修改我们前面说的Color.kt、Type.kt、Shape.kt三个文件,不用动Theme.kt(除非有特殊需求),我们一步步来,全程实战,代码可直接复制。
3.1 自定义颜色(最核心,必改)
颜色是主题最核心的部分,我们以“主色改为蓝色”为例,修改Color.kt,同时调整对应的onXXX颜色,保证对比度:
// 自定义浅色模式颜色(主色改为蓝色)
val lightColorScheme = lightColorScheme(
primary = Color(0xFF2196F3), // 主色:蓝色(常用的天蓝色)
onPrimary = Color.White, // 主色上的文字:白色(对比明显)
primaryContainer = Color(0xFFD1E7FF), // 主色容器:淡蓝色
onPrimaryContainer = Color(0xFF003566),// 主色容器上的文字:深蓝色
secondary = Color(0xFF03DAC5), // 次要色:青色(辅助主色)
onSecondary = Color.Black, // 次要色上的文字:黑色
background = Color(0xFFF5F5F5), // 页面背景:浅灰色(比纯白更柔和)
onBackground = Color(0xFF1C1B1F), // 背景文字:深灰色(比纯黑更柔和)
surface = Color(0xFFFFFFFF), // 表面色:白色(卡片、按钮背景)
onSurface = Color(0xFF1C1B1F), // 表面文字:深灰色
error = Color(0xFFB3261E), // 错误色:红色(不变)
onError = Color.White // 错误色文字:白色
)
// 自定义深色模式颜色(对应浅色模式,保持风格统一)
val darkColorScheme = darkColorScheme(
primary = Color(0xFF64B5F6), // 深色模式主色:淡蓝色(不刺眼)
onPrimary = Color(0xFF000000), // 主色文字:黑色
primaryContainer = Color(0xFF004B97),// 主色容器:深蓝色
onPrimaryContainer = Color(0xFFE1F5FE),// 主色容器文字:淡蓝色
secondary = Color(0xFF64FFDA), // 次要色:淡青色
onSecondary = Color(0xFF000000), // 次要色文字:黑色
background = Color(0xFF121212), // 背景:纯黑色(深色模式常用)
onBackground = Color(0xFFE1E1E1), // 背景文字:浅灰色
surface = Color(0xFF1E1E1E), // 表面色:深灰色(卡片背景)
onSurface = Color(0xFFE1E1E1), // 表面文字:浅灰色
error = Color(0xFFF2B8B5), // 错误色:淡红色
onError = Color(0xFF601410) // 错误色文字:深灰色
)
💡 新手小贴士:自定义颜色时,不用自己瞎找色值,推荐用Android Studio自带的颜色选择器,或者去Material Design官网找规范的颜色,保证颜色搭配美观、对比度足够,避免出现“深色背景+深色文字”看不清的情况。
3.2 自定义字体(统一全局字体)
默认字体虽然够用,但有时候我们想调整字体大小、粗细,或者统一字体风格,这时候就修改Type.kt。我们以“调整常用字体大小”为例,修改后更贴合手机端的阅读体验:
// 自定义字体样式
val Typography = Typography(
// 页面大标题(比如启动页标题)
displayLarge = TextStyle(
fontSize = 28.sp,
fontWeight = FontWeight.Bold,
lineHeight = 36.sp,
letterSpacing = 0.sp
),
// 页面小标题(比如页面顶部标题)
headlineSmall = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
lineHeight = 26.sp,
letterSpacing = 0.sp
),
// 卡片标题、按钮文字
titleLarge = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
lineHeight = 24.sp,
letterSpacing = 0.sp
),
// 正文文字(最常用,比如列表项、详情页正文)
bodyLarge = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
lineHeight = 22.sp,
letterSpacing = 0.25.sp
),
// 次要正文、提示文字(比如输入框提示、备注)
bodyMedium = TextStyle(
fontSize = 13.sp,
fontWeight = FontWeight.Normal,
lineHeight = 19.sp,
letterSpacing = 0.25.sp
),
// 小提示文字(比如标签、时间)
labelLarge = TextStyle(
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
lineHeight = 18.sp,
letterSpacing = 0.1.sp
)
)
重点说明:字体大小建议用sp(可跟随系统字体大小调整),不要用dp;行高建议设置为字体大小的1.4-1.6倍,这样阅读起来更舒适,比如15.sp的正文,行高设置为22.sp。
3.3 自定义形状(统一圆角)
圆角是提升UI精致度的关键,默认的8.dp圆角可能太生硬,我们修改Shape.kt,把常用的medium圆角改成12.dp,让卡片、按钮更圆润:
// 自定义形状(圆角)
val Shapes = Shapes(
small = RoundedCornerShape(6.dp), // 小控件:小按钮、图标(略大一点,更圆润)
medium = RoundedCornerShape(12.dp), // 中等控件:卡片、按钮、输入框(最常用,改成12.dp)
large = RoundedCornerShape(20.dp) // 大控件:大卡片、弹窗(更大的圆角,更有层次感)
)
✅ 效果说明:修改完成后,所有引用MaterialTheme.shapes.medium的组件,圆角都会变成12.dp,比如我们之前写的按钮、卡片,不用改任何页面代码,全局自动同步。
3.4 自定义主题完整实战(可直接复制到项目)
整合上面的自定义颜色、字体、形状,我们得到一套完整的主题代码,新手可以直接复制到自己的项目中,替换默认的主题文件,直接使用:
1. 完整Color.kt
package com.example.composelearn.ui.theme // 替换成自己的包名
import androidx.compose.ui.graphics.Color
val lightColorScheme = lightColorScheme(
primary = Color(0xFF2196F3),
onPrimary = Color.White,
primaryContainer = Color(0xFFD1E7FF),
onPrimaryContainer = Color(0xFF003566),
secondary = Color(0xFF03DAC5),
onSecondary = Color.Black,
background = Color(0xFFF5F5F5),
onBackground = Color(0xFF1C1B1F),
surface = Color(0xFFFFFFFF),
onSurface = Color(0xFF1C1B1F),
error = Color(0xFFB3261E),
onError = Color.White
)
val darkColorScheme = darkColorScheme(
primary = Color(0xFF64B5F6),
onPrimary = Color(0xFF000000),
primaryContainer = Color(0xFF004B97),
onPrimaryContainer = Color(0xFFE1F5FE),
secondary = Color(0xFF64FFDA),
onSecondary = Color(0xFF000000),
background = Color(0xFF121212),
onBackground = Color(0xFFE1E1E1),
surface = Color(0xFF1E1E1E),
onSurface = Color(0xFFE1E1E1),
error = Color(0xFFF2B8B5),
onError = Color(0xFF601410)
)
2. 完整Type.kt
package com.example.composelearn.ui.theme // 替换成自己的包名
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
val Typography = Typography(
displayLarge = TextStyle(
fontSize = 28.sp,
fontWeight = FontWeight.Bold,
lineHeight = 36.sp,
letterSpacing = 0.sp
),
headlineSmall = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
lineHeight = 26.sp,
letterSpacing = 0.sp
),
titleLarge = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
lineHeight = 24.sp,
letterSpacing = 0.sp
),
bodyLarge = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
lineHeight = 22.sp,
letterSpacing = 0.25.sp
),
bodyMedium = TextStyle(
fontSize = 13.sp,
fontWeight = FontWeight.Normal,
lineHeight = 19.sp,
letterSpacing = 0.25.sp
),
labelLarge = TextStyle(
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
lineHeight = 18.sp,
letterSpacing = 0.1.sp
)
)
3. 完整Shape.kt
package com.example.composelearn.ui.theme // 替换成自己的包名
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
small = RoundedCornerShape(6.dp),
medium = RoundedCornerShape(12.dp),
large = RoundedCornerShape(20.dp)
)
4. 主题使用演示(完整页面)
@Composable
fun ThemeDemo() {
// 主题已经在MainActivity中包裹,这里直接使用主题样式
Column(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.padding(16.dp),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally
) {
// 页面标题(引用主题字体、颜色)
Text(
text = "自定义主题演示",
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(bottom = 20.dp)
)
// 主题按钮(引用主题主色、圆角、字体)
Button(
onClick = {},
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary),
shape = MaterialTheme.shapes.medium,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = "主题主按钮",
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onPrimary
)
}
// 次要按钮(引用主题次要色)
Button(
onClick = {},
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.secondary),
shape = MaterialTheme.shapes.medium,
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp)
) {
Text(
text = "主题次要按钮",
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSecondary
)
}
// 主题卡片(引用主题表面色、圆角、阴影)
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp)
.background(MaterialTheme.colorScheme.surface, MaterialTheme.shapes.medium)
.shadow(4.dp, MaterialTheme.shapes.medium)
.padding(16.dp)
) {
Column {
Text(
text = "主题卡片标题",
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSurface
)
Text(
text = "这是一张使用主题样式的卡片,颜色、圆角、字体都来自主题,修改主题即可全局同步。",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(top = 8.dp)
)
}
}
// 错误提示(引用主题错误色)
Text(
text = "这是一条错误提示",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.error,
modifier = Modifier.padding(top = 20.dp)
)
}
}
✅ 效果说明:运行这个页面,就能看到所有组件都使用了我们自定义的主题样式,按钮是蓝色主色、12.dp圆角,卡片是白色背景、12.dp圆角,字体大小也符合我们的自定义设置,切换系统深色模式,所有颜色会自动切换到深色配置,非常方便。
四、进阶技巧:手动切换浅色/深色模式
默认主题是跟随系统切换浅色/深色模式的,但有时候我们想在APP内部添加一个“切换按钮”,让用户手动控制模式,比如添加一个开关,点击切换浅色和深色,这时候就需要用到状态管理,结合主题实现。
我们结合上一篇学的状态管理(remember、mutableStateOf),实现手动切换主题模式,代码可直接复制运行:
@Composable
fun ThemeSwitchDemo() {
// 定义状态,控制是否开启深色模式(默认跟随系统)
var isDarkMode by remember {
mutableStateOf(isSystemInDarkTheme())
}
// 包裹主题,手动传入darkTheme参数
MyAppTheme(darkTheme = isDarkMode) {
Column(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// 显示当前模式
Text(
text = if (isDarkMode) "当前是深色模式" else "当前是浅色模式",
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(bottom = 20.dp)
)
// 切换主题的开关
Switch(
checked = isDarkMode,
onCheckedChange = { isDarkMode = it },
modifier = Modifier.padding(bottom = 20.dp)
)
// 测试卡片:切换模式时,颜色会自动变化
Box(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surface, MaterialTheme.shapes.medium)
.shadow(4.dp, MaterialTheme.shapes.medium)
.padding(16.dp)
) {
Text(
text = "切换模式,我会变色哦",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurface
)
}
}
}
}
✅ 效果说明:运行后,点击开关,就能手动切换浅色/深色模式,页面中的所有组件(文本、卡片、背景)都会自动适配对应的主题颜色,不用写额外的适配代码,这就是主题的强大之处。
💡 进阶小贴士:如果想让用户切换的主题模式“持久化”(比如关闭APP再打开,还是上次设置的模式),可以结合rememberSaveable,或者用DataStore存储状态,这个我们后续讲ViewModel的时候会详细说。
五、新手写主题的常见避坑指南(非常真实)
第六篇避坑重点:主题看似简单,但新手很容易踩坑,尤其是细节问题,以下6个坑提前避开,少走大量弯路,避免后期返工
- 颜色写死,不用主题:这是新手最容易犯的错,比如直接写Color(0xFF2196F3),而不用MaterialTheme.colorScheme.primary,后期修改主色时,只能全局搜索修改,极其麻烦。记住:只要是UI中用到的颜色,除了特殊场景,都要从主题中引用。
- 忽略onXXX颜色,对比度不够:比如主色用深色,onPrimary也用深色,导致文字看不清,违反Material3设计规范。新手一定要记住,每个颜色都要搭配对应的onXXX颜色,保证文字和背景的对比度。
- 字体写死大小,不用主题样式:比如直接写fontSize = 16.sp,而不用MaterialTheme.typography.bodyLarge,后期想统一调整字体大小,只能逐行修改。正确做法是,所有文本都引用主题中的字体样式。
- 圆角写死,不用主题形状:比如每个卡片都写RoundedCornerShape(12.dp),而不用MaterialTheme.shapes.medium,后期想修改圆角大小,需要全局修改,非常繁琐。
- 页面不包裹主题:忘记在MainActivity中用MyAppTheme包裹页面,导致无法引用主题中的样式,出现“MaterialTheme找不到”的错误,或者样式不生效。
- 自定义主题时,只改浅色模式,不改深色模式:导致切换到深色模式时,样式混乱,比如背景是黑色,文字也是黑色,看不清内容。正确做法是,自定义颜色时,浅色和深色模式要对应修改,保持风格统一。
一句话总结:能走主题,就别写死,养成引用主题样式的习惯,后期维护会轻松很多,也能让你的代码更规范、更专业。
六、本篇总结 + 下篇预告
6.1 本篇核心收获
- 理解了Material3主题的核心作用:统一管理APP的颜色、字体、形状,提升可维护性,支持深色模式。
- 掌握了主题的基本结构:Color(颜色)、Typography(字体)、Shape(形状),知道每个部分的作用和用法。
- 学会了自定义主题:能自定义颜色(主色、次要色等)、字体(大小、粗细)、形状(圆角),并应用到项目中。
- 掌握了手动切换浅色/深色模式的方法,结合状态管理,实现主题模式的灵活控制。
- 避开了新手写主题的常见坑,养成了规范引用主题样式的习惯,为后续项目开发打下基础。
6.2 下篇内容预告
本篇我们搞定了主题,让APP的样式统一、好维护,但现在我们写的所有页面,都是单个页面,还不能实现页面之间的跳转——比如从主页面跳转到详情页,从列表页跳转到设置页。下一篇我们将学习Compose的导航组件:第七篇:导航组件Navigation Compose,学会页面跳转、返回、传参、栈管理,真正实现一个多页面的APP架构,迈出正式项目开发的关键一步。
七、系列更新进度(建议收藏追更)
- 第一篇:零基础入门,环境搭建+第一个Compose页面(已更新)
- 第二篇:基础组件+三大核心布局,实战简单UI(已更新)
- 第三篇:Modifier修饰符全解,UI样式灵活控制(已更新)
- 第四篇:Compose状态管理核心(remember、mutableStateOf)(已更新)
- 第五篇:列表组件LazyColumn、LazyRow(替代RecyclerView)(已更新)
- 第六篇:Material3主题、样式与自定义主题(当前)
- 第七篇:导航组件Navigation Compose
- 第八篇:ViewModel+Compose架构实战
- 第九篇:Compose动画基础与常用交互
- 第十篇:综合实战:仿写简单常用页面,吃透全流程
本篇的自定义主题代码,建议大家直接复制到自己的项目中,替换默认主题,动手运行一遍,感受主题的便捷性。尤其是手动切换深色模式的案例,能帮你更好地理解主题和状态管理的结合。如果文章对你有帮助,欢迎点赞、收藏、关注三连,评论区留下你的学习疑问或遇到的问题,下篇导航组件内容正在加急更新,带你继续吃透Compose核心~