Skip to content

列表高级交互 (Swipe/Refresh)

源:列表交互

本章介绍列表常见的两种交互:下拉刷新和侧滑删除。

1. 下拉刷新 (PullToRefresh)

API 变更

在 Material 3 1.3.0 之前,使用的是 PullToRefreshContainer + nestedScroll 的复杂写法。 从 Material 3 1.3.0 开始,Google 推荐使用更简单的 PullToRefreshBox

kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PullToRefreshSample() {
    var isRefreshing by remember { mutableStateOf(false) }
    val scope = rememberCoroutineScope()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = {
            scope.launch {
                isRefreshing = true
                delay(2000) // 模拟网络请求
                isRefreshing = false
            }
        }
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(50) {
                ListItem(headlineContent = { Text("Item $it") })
            }
        }
    }
}

2. 侧滑删除 (SwipeToDismiss)

M3 提供了 SwipeToDismissBox (旧版叫 SwipeToDismiss),支持从左或从右滑动。

kotlin
@Composable
fun SwipeToDismissList() {
    val items = remember { mutableStateListOf("Item A", "Item B", "Item C") }

    LazyColumn {
        items(items, key = { it }) { item ->
            SwipeToDismissItem(
                item = item,
                onRemove = { items.remove(item) }
            )
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SwipeToDismissItem(item: String, onRemove: () -> Unit) {
    val dismissState = rememberSwipeToDismissBoxState(
        confirmValueChange = { value ->
            if (value == SwipeToDismissBoxValue.EndToStart) {
                onRemove()
                true
            } else {
                false
            }
        }
    )

    SwipeToDismissBox(
        state = dismissState,
        backgroundContent = {
            // 背景层:滑动时显示的内容 (如红色垃圾桶)
            val color = if (dismissState.dismissDirection == SwipeToDismissBoxValue.EndToStart) {
                Color.Red
            } else Color.Transparent
            
            Box(
                Modifier
                    .fillMaxSize()
                    .background(color)
                    .padding(horizontal = 20.dp),
                contentAlignment = Alignment.CenterEnd
            ) {
                Icon(Icons.Default.Delete, contentDescription = "Delete", tint = Color.White)
            }
        },
        content = {
            // 前景层:实际列表项内容
            ListItem(
                headlineContent = { Text(item) },
                modifier = Modifier.background(Color.White)
            )
        }
    )
}