首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >前端开发者的 Kotlin 之旅:Compose Multiplatform 实战Material 3组件库

前端开发者的 Kotlin 之旅:Compose Multiplatform 实战Material 3组件库

原创
作者头像
骑猪耍太极
发布2025-10-15 19:54:27
发布2025-10-15 19:54:27
1640
举报

在掌握了 Compose 的基础组件、布局系统和状态管理后,接下来学习下 Material 3 设计系统。Material 3 是 Google 最新的设计语言,对于习惯了 Bootstrap、Ant Design 或 Material-UI 的前端开发者来说,理解 Material 3 的设计理念和组件体系既有相似性,也有其独特的创新之处。

Material 3 解决的核心问题

设计一致性挑战

在前端开发中,我们经常面临这些问题:

  • 跨平台设计不一致:Web、移动端、桌面端各自为政
  • 主题切换复杂:暗色模式需要大量 CSS 变量和媒体查询
  • 组件状态管理:按钮的 hover、focus、disabled 状态需要手动处理
  • 无障碍访问:色彩对比度、键盘导航等需要额外关注

Material 3 的解决方案

Material 3 通过以下方式解决了这些痛点:

统一的设计语言

代码语言:kotlin
复制
// 一套代码,多端一致的视觉效果
MaterialTheme {
    Button(onClick = {}) { Text("按钮") }  // 自动适配所有平台
}

智能的颜色系统

代码语言:kotlin
复制
// 自动生成完整的颜色方案,包括暗色模式
lightColorScheme(primary = Color(0xFF6750A4))  // 自动计算 onPrimary、primaryContainer 等

内置的无障碍支持

代码语言:kotlin
复制
// 自动处理色彩对比度、触摸目标大小等
Button(onClick = {}) {
    Text("操作")  // 自动满足 WCAG 标准
}

适用场景

Material 3 特别适合以下项目:

  1. 跨平台应用:需要在 Android、iOS、Web、Desktop 保持一致体验
  2. 企业级应用:需要完整的设计系统和组件库
  3. 快速原型:利用现成组件快速构建 MVP
  4. 国际化产品:需要支持多语言、多主题的全球化应用

快速体验

在深入学习之前,让我们先运行第四个模块来直观感受 Material 3 组件:

克隆项目

代码语言:bash
复制
git clone https://cnb.cool/cool-cc/learn-jetpack-compose
cd learn-jetpack-compose

Web 版本

代码语言:bash
复制
./gradlew :lesson-04-material3-components:wasmJsBrowserDevelopmentRun

运行后可以看到完整的 Material 3 组件展示,包括主题切换、各种按钮、输入框、颜色系统等。

Lesson 04: Material 3组件库
Lesson 04: Material 3组件库

设计系统对比

前端 UI 库 vs Material 3

前端开发

Material 3

说明

Ant Design

Material 3

完整的设计系统

theme.js

MaterialTheme

主题配置系统

Button variants

Button/FilledTonalButton/OutlinedButton

按钮变体

Input/Form

OutlinedTextField/TextField

输入组件

CSS Variables

ColorScheme

颜色令牌系统

@media (prefers-color-scheme)

darkTheme 参数

暗色模式支持

Material 3 主题系统

颜色系统架构

Material 3 的颜色系统比传统前端 UI 库更加系统化。项目中的 CustomMaterial3Theme.kt 展示了完整的颜色定义:

代码语言:kotlin
复制
// 浅色主题配色
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
复制
/* CSS 方式 */
:root {
  --primary: #6750A4;
  --on-primary: #FFFFFF;
  --surface: #FFFBFE;
}

[data-theme="dark"] {
  --primary: #D0BCFF;
  --on-primary: #381E72;
}

主题切换实现

项目中的主题切换比前端实现更加优雅:

代码语言:kotlin
复制
@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 展示了完整用法:

代码语言:kotlin
复制
// 1. 填充按钮 - 最高优先级
Button(onClick = {}) {
    ChineseText("主要操作")
}

// 2. 色调按钮 - 次要操作
FilledTonalButton(onClick = {}) {
    ChineseText("次要操作")
}

// 3. 轮廓按钮 - 中等优先级
OutlinedButton(onClick = {}) {
    ChineseText("轮廓按钮")
}

// 4. 文本按钮 - 最低优先级
TextButton(onClick = {}) {
    ChineseText("文本按钮")
}

与前端按钮对比

代码语言:jsx
复制
// React + Ant Design
<Button type="primary">主要操作</Button>
<Button type="default">次要操作</Button>
<Button type="dashed">轮廓按钮</Button>
<Button type="link">文本按钮</Button>

输入组件系统

Material 3 的输入组件设计更加注重用户体验,项目中的 InputComponentsExamples.kt 展示了各种场景:

代码语言:kotlin
复制
// 基础输入框
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
            )
        }
    }
)

与前端输入框对比

代码语言:jsx
复制
// React 实现
<Input.Password 
    placeholder="请输入密码"
    iconRender={(visible) => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)}
/>

颜色系统展示

项目中的 ColorSystemExamples.kt 展示了 Material 3 的完整颜色体系:

代码语言:kotlin
复制
// 主色组
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 展示了完整的用法:

代码语言:kotlin
复制
// 复选框组合
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 })

与前端表单组件对比

代码语言:jsx
复制
// 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 展示了实际用法:

代码语言:kotlin
复制
// 底部导航栏
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 }
        )
    }
}

与前端导航对比

代码语言:jsx
复制
// 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 展示了如何创建完整的自定义主题:

代码语言:kotlin
复制
// 自定义浅色主题
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
    )
}

字体排版系统

代码语言:kotlin
复制
// 自定义字体排版
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
    )
)

实际应用场景

表单构建

结合多种组件构建复杂表单:

代码语言:kotlin
复制
@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("保存资料")
        }
    }
}

卡片和列表组件

代码语言:kotlin
复制
@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("加入购物车")
                }
            }
        }
    }
}

响应式设计实践

自适应布局

代码语言:kotlin
复制
@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()
                }
            }
        }
    }
}

主题适配

代码语言:kotlin
复制
@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 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Material 3 解决的核心问题
    • 设计一致性挑战
    • Material 3 的解决方案
    • 适用场景
  • 快速体验
    • 克隆项目
    • Web 版本
  • 设计系统对比
    • 前端 UI 库 vs Material 3
  • Material 3 主题系统
    • 颜色系统架构
    • 主题切换实现
  • 核心组件解析
    • 按钮组件系统
    • 输入组件系统
    • 颜色系统展示
  • 选择与控制组件
  • 导航组件系统
  • 高级主题定制
    • 自定义颜色方案
    • 字体排版系统
  • 实际应用场景
    • 表单构建
    • 卡片和列表组件
  • 响应式设计实践
    • 自适应布局
    • 主题适配
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档