
在掌握了 Compose 的基础组件、布局系统和状态管理后,接下来学习下 Material 3 设计系统。Material 3 是 Google 最新的设计语言,对于习惯了 Bootstrap、Ant Design 或 Material-UI 的前端开发者来说,理解 Material 3 的设计理念和组件体系既有相似性,也有其独特的创新之处。
在前端开发中,我们经常面临这些问题:
Material 3 通过以下方式解决了这些痛点:
统一的设计语言:
// 一套代码,多端一致的视觉效果
MaterialTheme {
Button(onClick = {}) { Text("按钮") } // 自动适配所有平台
}智能的颜色系统:
// 自动生成完整的颜色方案,包括暗色模式
lightColorScheme(primary = Color(0xFF6750A4)) // 自动计算 onPrimary、primaryContainer 等内置的无障碍支持:
// 自动处理色彩对比度、触摸目标大小等
Button(onClick = {}) {
Text("操作") // 自动满足 WCAG 标准
}Material 3 特别适合以下项目:
在深入学习之前,让我们先运行第四个模块来直观感受 Material 3 组件:
git clone https://cnb.cool/cool-cc/learn-jetpack-compose
cd learn-jetpack-compose./gradlew :lesson-04-material3-components:wasmJsBrowserDevelopmentRun运行后可以看到完整的 Material 3 组件展示,包括主题切换、各种按钮、输入框、颜色系统等。

前端开发 | Material 3 | 说明 |
|---|---|---|
|
| 完整的设计系统 |
|
| 主题配置系统 |
|
| 按钮变体 |
|
| 输入组件 |
|
| 颜色令牌系统 |
|
| 暗色模式支持 |
Material 3 的颜色系统比传统前端 UI 库更加系统化。项目中的 CustomMaterial3Theme.kt 展示了完整的颜色定义:
// 浅色主题配色
private val LightColorScheme = lightColorScheme(
primary = Color(0xFF6750A4),
onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFFEADDFF),
onPrimaryContainer = Color(0xFF21005D),
secondary = Color(0xFF625B71),
tertiary = Color(0xFF7D5260),
surface = Color(0xFFFFFBFE),
surfaceVariant = Color(0xFFE7E0EC),
background = Color(0xFFFFFBFE)
)
// 暗色主题配色
private val DarkColorScheme = darkColorScheme(
primary = Color(0xFFD0BCFF),
onPrimary = Color(0xFF381E72),
// ... 完整的暗色配色
)与 CSS 自定义属性对比:
/* CSS 方式 */
:root {
--primary: #6750A4;
--on-primary: #FFFFFF;
--surface: #FFFBFE;
}
[data-theme="dark"] {
--primary: #D0BCFF;
--on-primary: #381E72;
}项目中的主题切换比前端实现更加优雅:
@Composable
fun Material3App() {
var isDarkMode by remember { mutableStateOf(false) }
CustomMaterial3Theme(darkTheme = isDarkMode) {
// 主题切换开关
Switch(
checked = isDarkMode,
onCheckedChange = { isDarkMode = it }
)
// 所有子组件自动应用新主题
Surface(color = MaterialTheme.colorScheme.background) {
// UI 内容
}
}
}Material 3 提供了四种按钮层次,项目中的 BasicMaterialComponentsExamples.kt 展示了完整用法:
// 1. 填充按钮 - 最高优先级
Button(onClick = {}) {
ChineseText("主要操作")
}
// 2. 色调按钮 - 次要操作
FilledTonalButton(onClick = {}) {
ChineseText("次要操作")
}
// 3. 轮廓按钮 - 中等优先级
OutlinedButton(onClick = {}) {
ChineseText("轮廓按钮")
}
// 4. 文本按钮 - 最低优先级
TextButton(onClick = {}) {
ChineseText("文本按钮")
}与前端按钮对比:
// React + Ant Design
<Button type="primary">主要操作</Button>
<Button type="default">次要操作</Button>
<Button type="dashed">轮廓按钮</Button>
<Button type="link">文本按钮</Button>Material 3 的输入组件设计更加注重用户体验,项目中的 InputComponentsExamples.kt 展示了各种场景:
// 基础输入框
OutlinedTextField(
value = textValue,
onValueChange = { textValue = it },
label = { ChineseText("基础输入框") },
placeholder = { ChineseText("请输入文本") }
)
// 密码输入框(带可见性切换)
OutlinedTextField(
value = passwordValue,
onValueChange = { passwordValue = it },
label = { ChineseText("密码") },
visualTransformation = if (passwordVisible)
VisualTransformation.None
else
PasswordVisualTransformation(),
trailingIcon = {
IconButton(onClick = { passwordVisible = !passwordVisible }) {
Icon(
if (passwordVisible) Icons.Default.Visibility
else Icons.Default.VisibilityOff,
contentDescription = null
)
}
}
)与前端输入框对比:
// React 实现
<Input.Password
placeholder="请输入密码"
iconRender={(visible) => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)}
/>项目中的 ColorSystemExamples.kt 展示了 Material 3 的完整颜色体系:
// 主色组
Card(colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primaryContainer
)) {
Text(
text = "Primary Container",
color = MaterialTheme.colorScheme.onPrimaryContainer
)
}
// 表面色组
Surface(
color = MaterialTheme.colorScheme.surfaceVariant,
contentColor = MaterialTheme.colorScheme.onSurfaceVariant
) {
Text("Surface Variant")
}Material 3 的选择组件提供了丰富的交互状态,项目中的 SelectionComponentsExamples.kt 展示了完整的用法:
// 复选框组合
var checked by remember { mutableStateOf(false) }
Row(verticalAlignment = Alignment.CenterVertically) {
Checkbox(checked = checked, onCheckedChange = { checked = it })
ChineseText("同意用户协议")
}
// 单选按钮组
val radioOptions = listOf("红色", "绿色", "蓝色")
var selectedOption by remember { mutableStateOf(radioOptions[0]) }
Column(modifier = Modifier.selectableGroup()) {
radioOptions.forEach { option ->
Row(verticalAlignment = Alignment.CenterVertically) {
RadioButton(
selected = (option == selectedOption),
onClick = { selectedOption = option }
)
ChineseText(option)
}
}
}
// 滑块和开关
var sliderValue by remember { mutableStateOf(50f) }
Slider(
value = sliderValue,
onValueChange = { sliderValue = it },
valueRange = 0f..100f
)
var switchState by remember { mutableStateOf(false) }
Switch(checked = switchState, onCheckedChange = { switchState = it })与前端表单组件对比:
// React + Ant Design
<Checkbox onChange={handleChange}>同意用户协议</Checkbox>
<Radio.Group value={selectedOption} onChange={handleRadioChange}>
<Radio value="red">红色</Radio>
</Radio.Group>
<Slider defaultValue={50} />
<Switch checked={switchState} onChange={setSwitchState} />Material 3 的导航组件为应用提供了完整的导航解决方案,项目中的 NavigationComponentsExamples.kt 展示了实际用法:
// 底部导航栏
val navItems = listOf(
"首页" to Icons.Default.Home,
"搜索" to Icons.Default.Search,
"收藏" to Icons.Default.Favorite,
"设置" to Icons.Default.Settings
)
var selectedNavItem by remember { mutableStateOf(0) }
NavigationBar {
navItems.forEachIndexed { index, (label, icon) ->
NavigationBarItem(
icon = { Icon(icon, contentDescription = label) },
label = { ChineseText(label) },
selected = selectedNavItem == index,
onClick = { selectedNavItem = index }
)
}
}
// 导航抽屉
val drawerItems = listOf(
"首页" to Icons.Default.Home,
"个人资料" to Icons.Default.Person,
"消息" to Icons.Default.Email
)
Column {
drawerItems.forEachIndexed { index, (label, icon) ->
NavigationDrawerItem(
icon = { Icon(icon, contentDescription = label) },
label = { ChineseText(label) },
selected = selectedDrawerItem == index,
onClick = { selectedDrawerItem = index }
)
}
}与前端导航对比:
// React + Ant Design
<Tabs activeKey={selectedTab} onChange={setSelectedTab}>
<TabPane tab={<><HomeOutlined />首页</>} key="home" />
<TabPane tab={<><SearchOutlined />搜索</>} key="search" />
</Tabs>
<Drawer open={drawerOpen} onClose={closeDrawer}>
<Menu selectedKeys={[selectedKey]}>
<Menu.Item key="home" icon={<HomeOutlined />}>首页</Menu.Item>
</Menu>
</Drawer>项目中的 CustomMaterial3Theme.kt 展示了如何创建完整的自定义主题:
// 自定义浅色主题
private val LightColorScheme = lightColorScheme(
primary = Color(0xFF6750A4),
onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFFEADDFF),
onPrimaryContainer = Color(0xFF21005D),
secondary = Color(0xFF625B71),
tertiary = Color(0xFF7D5260),
surface = Color(0xFFFFFBFE),
surfaceVariant = Color(0xFFE7E0EC),
background = Color(0xFFFFFBFE)
)
// 主题应用
@Composable
fun CustomMaterial3Theme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
shapes = Shapes,
content = content
)
}// 自定义字体排版
val Typography = Typography(
displayLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 57.sp,
lineHeight = 64.sp,
letterSpacing = (-0.25).sp
),
headlineLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 32.sp,
lineHeight = 40.sp,
letterSpacing = 0.sp
)
)结合多种组件构建复杂表单:
@Composable
fun UserProfileForm() {
var name by remember { mutableStateOf("") }
var email by remember { mutableStateOf("") }
var age by remember { mutableStateOf(25f) }
var notifications by remember { mutableStateOf(true) }
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// 文本输入
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("姓名") },
modifier = Modifier.fillMaxWidth()
)
// 邮箱输入
OutlinedTextField(
value = email,
onValueChange = { email = it },
label = { Text("邮箱") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
modifier = Modifier.fillMaxWidth()
)
// 年龄滑块
Text("年龄: ${age.toInt()}")
Slider(
value = age,
onValueChange = { age = it },
valueRange = 18f..100f
)
// 通知开关
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("接收通知")
Switch(
checked = notifications,
onCheckedChange = { notifications = it }
)
}
// 提交按钮
Button(
onClick = { /* 提交逻辑 */ },
modifier = Modifier.fillMaxWidth()
) {
Text("保存资料")
}
}
}@Composable
fun ProductCard(product: Product) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
)
) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = product.name,
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurface
)
Text(
text = product.description,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "¥${product.price}",
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.primary
)
FilledTonalButton(onClick = { /* 添加到购物车 */ }) {
Icon(Icons.Default.Add, contentDescription = null)
Spacer(Modifier.width(4.dp))
Text("加入购物车")
}
}
}
}
}@Composable
fun ResponsiveLayout() {
BoxWithConstraints {
val isCompact = maxWidth < 600.dp
if (isCompact) {
// 小屏幕:垂直布局 + 底部导航
Column {
TopAppBar(title = { Text("应用标题") })
Box(modifier = Modifier.weight(1f)) {
MainContent()
}
NavigationBar {
// 底部导航项
}
}
} else {
// 大屏幕:水平布局 + 侧边导航
Row {
NavigationRail {
// 侧边导航项
}
Column(modifier = Modifier.weight(1f)) {
TopAppBar(title = { Text("应用标题") })
MainContent()
}
}
}
}
}@Composable
fun AdaptiveThemeDemo() {
var isDarkMode by remember { mutableStateOf(false) }
CustomMaterial3Theme(darkTheme = isDarkMode) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column {
// 主题切换控制
Card(
modifier = Modifier.padding(16.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = if (isDarkMode) "暗色模式" else "亮色模式",
style = MaterialTheme.typography.titleMedium
)
Switch(
checked = isDarkMode,
onCheckedChange = { isDarkMode = it }
)
}
}
// 其他UI组件会自动适配新主题
MainAppContent()
}
}
}
}通过本模块的学习,开源加深对 Material 3 设计系统的理解。相比传统的前端 UI 框架,Material 3 提供了更加系统化和类型安全的组件开发体验。对于前端开发者来说,Material 3 + Compose Multiplatform 开启了跨平台开发的新篇章。下一步,我将继续探索自定义组件的开发,学习如何构建更复杂的业务组件。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。