Jetpack Compose Surface 完全指南

Jetpack Compose Surface 完全指南

📋 目录

  1. 什么是Surface
  2. Surface的核心作用
  3. [Surface vs Box vs Card](#Surface vs Box vs Card)
  4. Surface的参数详解
  5. 使用场景
  6. 代码示例
  7. 最佳实践
  8. 常见问题

什么是Surface

Surface 是Jetpack Compose中Material Design的基础容器组件,它提供了一个"表面"来承载其他UI元素。

定义

kotlin 复制代码
@Composable
fun Surface(
    modifier: Modifier = Modifier,
    shape: Shape = RectangleShape,
    color: Color = MaterialTheme.colorScheme.surface,
    contentColor: Color = contentColorFor(color),
    tonalElevation: Dp = 0.dp,
    shadowElevation: Dp = 0.dp,
    border: BorderStroke? = null,
    content: @Composable () -> Unit
)

核心概念

Surface是Material Design中"表面"概念的实现:

  • 在Material Design中,UI元素被组织在不同的"表面"上
  • 每个表面都有自己的高度(elevation)
  • 表面可以有颜色、形状、边框等属性
  • 表面之间通过阴影和高度来表现层次关系

Surface的核心作用

1. 提供Material Design的表面语义 ⭐⭐⭐⭐⭐

Surface是Material Design体系中的基础概念,它让UI元素具有"物理表面"的特性。

kotlin 复制代码
// Surface提供了Material Design的表面语义
Surface(
    color = MaterialTheme.colorScheme.surface,
    tonalElevation = 2.dp
) {
    Text("我在一个表面上")
}

为什么重要?

  • 符合Material Design规范
  • 提供一致的视觉层次
  • 自动处理颜色和高度的关系

2. 管理颜色和内容颜色 ⭐⭐⭐⭐⭐

Surface会自动为内容提供合适的颜色,确保文字和图标在背景上清晰可见。

kotlin 复制代码
// Surface会自动计算contentColor
Surface(
    color = MaterialTheme.colorScheme.primary // 蓝色背景
) {
    // Text会自动使用白色,因为Surface计算出这是最佳对比色
    Text("自动使用合适的文字颜色")
}

自动颜色对比:

  • 深色背景 → 浅色文字
  • 浅色背景 → 深色文字
  • 遵循WCAG无障碍标准

3. 处理高度和阴影 ⭐⭐⭐⭐

Surface通过elevation(高度)来表现UI元素的层次关系。

kotlin 复制代码
// 不同的高度产生不同的视觉效果
Surface(
    tonalElevation = 4.dp,  // 色调高度:影响颜色
    shadowElevation = 4.dp  // 阴影高度:产生阴影
) {
    Text("我有高度")
}

两种高度:

  • tonalElevation(色调高度): 影响表面颜色,高度越高颜色越浅
  • shadowElevation(阴影高度): 产生阴影效果

4. 提供形状和边框 ⭐⭐⭐⭐

Surface可以轻松设置形状和边框。

kotlin 复制代码
Surface(
    shape = RoundedCornerShape(16.dp),  // 圆角
    border = BorderStroke(2.dp, Color.Blue)  // 边框
) {
    Text("圆角带边框")
}

5. 处理点击事件 ⭐⭐⭐⭐

Surface可以作为可点击的容器,并提供Material Design的涟漪效果。

kotlin 复制代码
Surface(
    onClick = { /* 处理点击 */ },
    color = MaterialTheme.colorScheme.primaryContainer
) {
    Text("点击我", modifier = Modifier.padding(16.dp))
}

6. 作为布局容器 ⭐⭐⭐

Surface可以作为布局的根容器,为整个屏幕或区域提供背景。

kotlin 复制代码
Surface(
    modifier = Modifier.fillMaxSize(),
    color = MaterialTheme.colorScheme.background
) {
    // 整个屏幕的内容
    Column { /* ... */ }
}

Surface vs Box vs Card

对比表格

特性 Surface Box Card
用途 Material表面容器 通用布局容器 卡片容器
Material语义 ✅ 有 ❌ 无 ✅ 有
自动颜色对比 ✅ 有 ❌ 无 ✅ 有
高度支持 ✅ 完整 ❌ 无 ✅ 有限
形状 ✅ 支持 ❌ 需手动 ✅ 默认圆角
边框 ✅ 支持 ❌ 需手动 ✅ 支持
点击涟漪 ✅ 内置 ❌ 需手动 ✅ 内置
性能 中等 最优 中等
使用场景 Material UI 纯布局 卡片内容

何时使用Surface

✅ 应该使用Surface的场景:

  • 需要Material Design语义
  • 需要自动颜色对比
  • 需要表现高度层次
  • 需要可点击的表面
  • 构建Material组件

❌ 不应该使用Surface的场景:

  • 纯粹的布局对齐(用Box)
  • 不需要Material语义
  • 追求极致性能(用Box)
  • 已经有Card等高级组件

代码对比

kotlin 复制代码
// 1. 使用Box - 纯布局
Box(
    modifier = Modifier
        .background(Color.Blue)
        .padding(16.dp)
) {
    Text("使用Box", color = Color.White) // 需要手动设置颜色
}

// 2. 使用Surface - Material语义
Surface(
    color = MaterialTheme.colorScheme.primary,
    tonalElevation = 2.dp
) {
    Text("使用Surface", modifier = Modifier.padding(16.dp))
    // 文字颜色自动适配
}

// 3. 使用Card - 卡片容器
Card(
    modifier = Modifier.fillMaxWidth()
) {
    Text("使用Card", modifier = Modifier.padding(16.dp))
}

Surface的参数详解

1. modifier: Modifier

控制Surface的大小、位置、内边距等。

kotlin 复制代码
Surface(
    modifier = Modifier
        .fillMaxWidth()      // 填充宽度
        .height(100.dp)      // 固定高度
        .padding(16.dp)      // 外边距
) { /* ... */ }

2. shape: Shape

定义Surface的形状。

kotlin 复制代码
// 矩形(默认)
Surface(shape = RectangleShape) { }

// 圆角矩形
Surface(shape = RoundedCornerShape(8.dp)) { }
Surface(shape = RoundedCornerShape(
    topStart = 16.dp,
    topEnd = 16.dp,
    bottomStart = 0.dp,
    bottomEnd = 0.dp
)) { }

// 圆形
Surface(shape = CircleShape) { }

// 切角
Surface(shape = CutCornerShape(8.dp)) { }

3. color: Color

Surface的背景颜色。

kotlin 复制代码
// 使用主题颜色
Surface(color = MaterialTheme.colorScheme.primary) { }
Surface(color = MaterialTheme.colorScheme.surface) { }
Surface(color = MaterialTheme.colorScheme.background) { }

// 使用自定义颜色
Surface(color = Color.Blue) { }
Surface(color = Color(0xFF6200EE)) { }

4. contentColor: Color

内容的默认颜色(文字、图标等)。

kotlin 复制代码
// 自动计算(推荐)
Surface(
    color = MaterialTheme.colorScheme.primary
    // contentColor会自动设置为onPrimary
) { }

// 手动指定
Surface(
    color = Color.Blue,
    contentColor = Color.White
) { }

5. tonalElevation: Dp

色调高度,影响Surface的颜色深浅。

kotlin 复制代码
// 不同的色调高度
Surface(tonalElevation = 0.dp) { }  // 原始颜色
Surface(tonalElevation = 1.dp) { }  // 稍微变浅
Surface(tonalElevation = 3.dp) { }  // 更浅
Surface(tonalElevation = 6.dp) { }  // 很浅

效果:

  • 高度越高,颜色越浅(浅色主题)
  • 高度越高,颜色越亮(深色主题)
  • 用于表现层次关系

6. shadowElevation: Dp

阴影高度,产生阴影效果。

kotlin 复制代码
// 不同的阴影高度
Surface(shadowElevation = 0.dp) { }  // 无阴影
Surface(shadowElevation = 2.dp) { }  // 轻微阴影
Surface(shadowElevation = 4.dp) { }  // 中等阴影
Surface(shadowElevation = 8.dp) { }  // 明显阴影

注意:

  • 阴影只在浅色背景上明显
  • 深色主题下阴影不明显
  • 可以与tonalElevation配合使用

7. border: BorderStroke?

边框样式。

kotlin 复制代码
// 添加边框
Surface(
    border = BorderStroke(1.dp, Color.Gray)
) { }

// 使用主题颜色
Surface(
    border = BorderStroke(
        width = 2.dp,
        color = MaterialTheme.colorScheme.outline
    )
) { }

// 无边框(默认)
Surface(border = null) { }

8. onClick: (() -> Unit)?

点击事件处理。

kotlin 复制代码
// 可点击的Surface
Surface(
    onClick = { 
        println("Surface被点击了")
    },
    color = MaterialTheme.colorScheme.primaryContainer
) {
    Text("点击我", modifier = Modifier.padding(16.dp))
}

特点:

  • 自动添加涟漪效果
  • 符合Material Design交互规范
  • 自动处理点击状态

使用场景

1. 作为屏幕根容器

kotlin 复制代码
@Composable
fun MyScreen() {
    Surface(
        modifier = Modifier.fillMaxSize(),
        color = MaterialTheme.colorScheme.background
    ) {
        // 屏幕内容
        Column {
            TopAppBar()
            Content()
        }
    }
}

2. 创建卡片式内容

kotlin 复制代码
Surface(
    modifier = Modifier
        .fillMaxWidth()
        .padding(16.dp),
    shape = RoundedCornerShape(12.dp),
    tonalElevation = 2.dp,
    shadowElevation = 4.dp
) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text("标题", style = MaterialTheme.typography.titleLarge)
        Text("内容", style = MaterialTheme.typography.bodyMedium)
    }
}

3. 创建可点击的列表项

kotlin 复制代码
Surface(
    onClick = { /* 处理点击 */ },
    modifier = Modifier.fillMaxWidth(),
    color = MaterialTheme.colorScheme.surfaceVariant
) {
    Row(
        modifier = Modifier.padding(16.dp),
        verticalAlignment = Alignment.CenterVertically
    ) {
        Icon(Icons.Default.Person, contentDescription = null)
        Spacer(Modifier.width(16.dp))
        Text("列表项")
    }
}

4. 创建浮动按钮

kotlin 复制代码
Surface(
    onClick = { /* 处理点击 */ },
    shape = CircleShape,
    color = MaterialTheme.colorScheme.primary,
    shadowElevation = 6.dp,
    modifier = Modifier.size(56.dp)
) {
    Box(contentAlignment = Alignment.Center) {
        Icon(
            Icons.Default.Add,
            contentDescription = "添加",
            tint = MaterialTheme.colorScheme.onPrimary
        )
    }
}

5. 创建对话框背景

kotlin 复制代码
Surface(
    shape = RoundedCornerShape(28.dp),
    color = MaterialTheme.colorScheme.surface,
    tonalElevation = 6.dp,
    modifier = Modifier.padding(16.dp)
) {
    Column(modifier = Modifier.padding(24.dp)) {
        Text("对话框标题")
        Spacer(Modifier.height(16.dp))
        Text("对话框内容")
        Spacer(Modifier.height(24.dp))
        Row {
            TextButton(onClick = { }) { Text("取消") }
            TextButton(onClick = { }) { Text("确定") }
        }
    }
}

6. 创建底部导航栏

kotlin 复制代码
Surface(
    color = MaterialTheme.colorScheme.surfaceContainer,
    tonalElevation = 3.dp
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(8.dp),
        horizontalArrangement = Arrangement.SpaceEvenly
    ) {
        NavigationItem("首页", Icons.Default.Home)
        NavigationItem("搜索", Icons.Default.Search)
        NavigationItem("我的", Icons.Default.Person)
    }
}

7. 创建标签/徽章

kotlin 复制代码
Surface(
    shape = RoundedCornerShape(12.dp),
    color = MaterialTheme.colorScheme.primaryContainer,
    tonalElevation = 0.dp
) {
    Text(
        text = "新",
        modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
        style = MaterialTheme.typography.labelSmall
    )
}

8. 创建分隔区域

kotlin 复制代码
Column {
    Surface(
        color = MaterialTheme.colorScheme.primaryContainer,
        tonalElevation = 1.dp
    ) {
        Text(
            "第一部分",
            modifier = Modifier.padding(16.dp)
        )
    }
    
    Spacer(Modifier.height(8.dp))
    
    Surface(
        color = MaterialTheme.colorScheme.secondaryContainer,
        tonalElevation = 1.dp
    ) {
        Text(
            "第二部分",
            modifier = Modifier.padding(16.dp)
        )
    }
}

代码示例

示例1: 基础使用

kotlin 复制代码
@Composable
fun BasicSurfaceExample() {
    Surface(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        color = MaterialTheme.colorScheme.surface,
        tonalElevation = 2.dp,
        shape = RoundedCornerShape(8.dp)
    ) {
        Text(
            text = "这是一个Surface",
            modifier = Modifier.padding(16.dp)
        )
    }
}

示例2: 不同高度对比

kotlin 复制代码
@Composable
fun ElevationExample() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        listOf(0, 1, 2, 3, 4, 6, 8, 12, 16).forEach { elevation ->
            Surface(
                modifier = Modifier.fillMaxWidth(),
                tonalElevation = elevation.dp,
                shadowElevation = elevation.dp,
                shape = RoundedCornerShape(8.dp)
            ) {
                Text(
                    text = "Elevation: ${elevation}dp",
                    modifier = Modifier.padding(16.dp)
                )
            }
        }
    }
}

示例3: 不同形状

kotlin 复制代码
@Composable
fun ShapeExample() {
    Row(
        modifier = Modifier.padding(16.dp),
        horizontalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        // 矩形
        Surface(
            modifier = Modifier.size(80.dp),
            shape = RectangleShape,
            color = MaterialTheme.colorScheme.primary
        ) { }
        
        // 圆角
        Surface(
            modifier = Modifier.size(80.dp),
            shape = RoundedCornerShape(16.dp),
            color = MaterialTheme.colorScheme.secondary
        ) { }
        
        // 圆形
        Surface(
            modifier = Modifier.size(80.dp),
            shape = CircleShape,
            color = MaterialTheme.colorScheme.tertiary
        ) { }
        
        // 切角
        Surface(
            modifier = Modifier.size(80.dp),
            shape = CutCornerShape(16.dp),
            color = MaterialTheme.colorScheme.error
        ) { }
    }
}

示例4: 可点击的Surface

kotlin 复制代码
@Composable
fun ClickableSurfaceExample() {
    var clickCount by remember { mutableStateOf(0) }
    
    Surface(
        onClick = { clickCount++ },
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        shape = RoundedCornerShape(12.dp),
        color = MaterialTheme.colorScheme.primaryContainer,
        tonalElevation = 2.dp
    ) {
        Column(
            modifier = Modifier.padding(24.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Icon(
                Icons.Default.TouchApp,
                contentDescription = null,
                modifier = Modifier.size(48.dp)
            )
            Spacer(Modifier.height(16.dp))
            Text(
                text = "点击次数: $clickCount",
                style = MaterialTheme.typography.titleLarge
            )
        }
    }
}

示例5: 带边框的Surface

kotlin 复制代码
@Composable
fun BorderSurfaceExample() {
    Surface(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        shape = RoundedCornerShape(12.dp),
        color = MaterialTheme.colorScheme.surface,
        border = BorderStroke(
            width = 2.dp,
            color = MaterialTheme.colorScheme.primary
        )
    ) {
        Text(
            text = "带边框的Surface",
            modifier = Modifier.padding(16.dp)
        )
    }
}

示例6: 组合使用

kotlin 复制代码
@Composable
fun ComplexSurfaceExample() {
    Surface(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        shape = RoundedCornerShape(16.dp),
        tonalElevation = 2.dp,
        shadowElevation = 4.dp
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            // 标题区域
            Surface(
                color = MaterialTheme.colorScheme.primaryContainer,
                shape = RoundedCornerShape(8.dp)
            ) {
                Text(
                    text = "标题",
                    modifier = Modifier.padding(12.dp),
                    style = MaterialTheme.typography.titleMedium
                )
            }
            
            Spacer(Modifier.height(12.dp))
            
            // 内容区域
            Text(
                text = "这是内容区域,展示了Surface的嵌套使用。",
                style = MaterialTheme.typography.bodyMedium
            )
            
            Spacer(Modifier.height(12.dp))
            
            // 按钮区域
            Row(
                horizontalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                Surface(
                    onClick = { },
                    shape = RoundedCornerShape(8.dp),
                    color = MaterialTheme.colorScheme.primary
                ) {
                    Text(
                        text = "确定",
                        modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
                        color = MaterialTheme.colorScheme.onPrimary
                    )
                }
                
                Surface(
                    onClick = { },
                    shape = RoundedCornerShape(8.dp),
                    border = BorderStroke(1.dp, MaterialTheme.colorScheme.outline)
                ) {
                    Text(
                        text = "取消",
                        modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
                    )
                }
            }
        }
    }
}

最佳实践

1. 使用主题颜色

✅ 推荐:

kotlin 复制代码
Surface(
    color = MaterialTheme.colorScheme.surface,
    contentColor = MaterialTheme.colorScheme.onSurface
)

❌ 不推荐:

kotlin 复制代码
Surface(
    color = Color.White,
    contentColor = Color.Black
)

2. 合理使用高度

✅ 推荐:

kotlin 复制代码
// 使用Material Design推荐的高度值
Surface(tonalElevation = 1.dp)  // 轻微提升
Surface(tonalElevation = 3.dp)  // 中等提升
Surface(tonalElevation = 6.dp)  // 明显提升

❌ 不推荐:

kotlin 复制代码
// 避免使用过大或奇怪的高度值
Surface(tonalElevation = 100.dp)
Surface(tonalElevation = 2.5.dp)

3. 避免过度嵌套

✅ 推荐:

kotlin 复制代码
Surface {
    Column {
        Text("内容1")
        Text("内容2")
    }
}

❌ 不推荐:

kotlin 复制代码
Surface {
    Surface {
        Surface {
            Text("过度嵌套")
        }
    }
}

4. 选择合适的形状

✅ 推荐:

kotlin 复制代码
// 根据内容选择合适的形状
Surface(shape = RoundedCornerShape(8.dp))  // 小圆角
Surface(shape = RoundedCornerShape(16.dp)) // 大圆角
Surface(shape = CircleShape)               // 圆形图标

5. 性能考虑

✅ 推荐:

kotlin 复制代码
// 简单布局使用Box
Box(modifier = Modifier.background(Color.Blue)) {
    Text("简单内容")
}

// 需要Material语义时使用Surface
Surface(
    color = MaterialTheme.colorScheme.primary,
    tonalElevation = 2.dp
) {
    Text("Material内容")
}

6. 无障碍支持

✅ 推荐:

kotlin 复制代码
Surface(
    onClick = { },
    modifier = Modifier.semantics {
        contentDescription = "点击查看详情"
        role = Role.Button
    }
) {
    Text("详情")
}

常见问题

Q1: Surface和Box有什么区别?

A:

  • Surface: Material Design容器,提供颜色、高度、形状等Material语义
  • Box: 纯布局容器,只负责定位和层叠,性能更好

选择建议:

  • 需要Material语义 → Surface
  • 纯布局对齐 → Box

Q2: tonalElevation和shadowElevation有什么区别?

A:

  • tonalElevation: 影响颜色,高度越高颜色越浅
  • shadowElevation: 产生阴影,高度越高阴影越明显

使用建议:

  • 通常两者设置相同值
  • 深色主题下主要用tonalElevation
  • 浅色主题下可以都用

Q3: 为什么我的Surface没有阴影?

A: 可能的原因:

  1. 使用了tonalElevation而不是shadowElevation
  2. 背景是深色的(阴影在深色背景上不明显)
  3. 阴影被其他元素遮挡
  4. elevation值太小

解决方案:

kotlin 复制代码
Surface(
    shadowElevation = 8.dp,  // 使用shadowElevation
    color = Color.White      // 确保背景是浅色
)

Q4: Surface的点击涟漪效果可以自定义吗?

A: 可以通过interactionSource自定义:

kotlin 复制代码
val interactionSource = remember { MutableInteractionSource() }

Surface(
    onClick = { },
    interactionSource = interactionSource,
    indication = rememberRipple(
        bounded = true,
        color = Color.Red
    )
)

Q5: 如何让Surface填充整个屏幕?

A:

kotlin 复制代码
Surface(
    modifier = Modifier.fillMaxSize(),
    color = MaterialTheme.colorScheme.background
) {
    // 内容
}

Q6: Surface可以设置渐变背景吗?

A: Surface本身不支持渐变,但可以组合使用:

kotlin 复制代码
Box(
    modifier = Modifier
        .fillMaxSize()
        .background(
            Brush.verticalGradient(
                colors = listOf(Color.Blue, Color.Purple)
            )
        )
) {
    Surface(
        color = Color.Transparent,
        contentColor = Color.White
    ) {
        Text("渐变背景上的内容")
    }
}

Q7: Surface和Card应该用哪个?

A:

  • Card: 专门用于卡片内容,有默认的圆角和高度
  • Surface: 更通用的容器,需要手动设置样式

选择建议:

  • 卡片式内容 → Card
  • 其他场景 → Surface

Q8: 如何实现Surface的动画效果?

A:

kotlin 复制代码
var isExpanded by remember { mutableStateOf(false) }

Surface(
    modifier = Modifier
        .fillMaxWidth()
        .animateContentSize(), // 添加动画
    tonalElevation = if (isExpanded) 4.dp else 1.dp,
    onClick = { isExpanded = !isExpanded }
) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text("标题")
        if (isExpanded) {
            Text("展开的内容")
        }
    }
}

总结

Surface的核心价值

  1. Material Design语义 - 提供标准的Material表面
  2. 自动颜色管理 - 确保内容颜色对比度
  3. 高度系统 - 表现UI层次关系
  4. 交互支持 - 内置点击和涟漪效果
  5. 灵活性 - 支持各种形状、边框、颜色

何时使用Surface

✅ 应该使用:

  • 构建Material Design UI
  • 需要自动颜色对比
  • 需要表现高度层次
  • 需要可点击的容器

❌ 不应该使用:

  • 纯粹的布局对齐
  • 追求极致性能
  • 已有更合适的组件(如Card)

学习建议

  1. 理解Material Design的表面概念
  2. 掌握tonalElevation和shadowElevation的区别
  3. 学会使用主题颜色
  4. 了解Surface与其他组件的关系
  5. 在实践中体会Surface的价值

参考资源


最后更新: 2026年1月31日

相关推荐
我命由我123453 天前
Android 广播 - 组件通信
android·java·开发语言·android studio·android jetpack·android-studio·android runtime
alexhilton4 天前
为什么你的 LazyColumn 仍然卡顿
android·kotlin·android jetpack
hai_android4 天前
Android 嵌套滑动冲突:内部拦截 vs 外部拦截实战
android·android studio·android jetpack
alexhilton8 天前
模块化Jetpack Compose架构
android·kotlin·android jetpack
hai_android8 天前
InputStage 责任链核心执行流程
android·android studio·android jetpack
传奇开心果编程9 天前
【现代声明式UI学与练】第4课 列表渲染与 key——如何高效渲染列表、key 的作用、列表重排时的状态保持
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程10 天前
【现代声明式UI学与练】第7课 组件通信——父传子、子传父、跨层级通信、状态提升与 Context
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程11 天前
【现代声明式UI学与练】第9课 性能优化——渲染优化、列表优化、内存优化、启动优化
学习·flutter·react native·ui·性能优化·swiftui·android jetpack
天空之城--13 天前
Android一周动态:Android 18首次官宣、Compose Material3 1.4转正(5趋势+5资讯)
android·人工智能·flutter·架构·android jetpack
传奇开心果编程13 天前
【现代声明式UI学与练】第1课 从命令式UI到声明式UI
学习·flutter·ui·swiftui·react·android jetpack