如何在LazyColumn列表中实现仅选中单个条目的功能
基于LazyColumn实现配送地址单选功能方案
核心实现逻辑很简单:选中状态统一在列表上层托管,仅记录当前选中项的唯一标识,绝对不要把选中状态存在单个列表Item内部,天然就能保证同一时间只有一个地址被选中。
1. 定义基础数据模型
地址数据必须带唯一主键,用来区分不同条目、判断选中状态:
data class DeliveryAddress( val id: String, // 地址唯一ID,一般用后端返回的地址主键 val contactName: String, val phone: String, val detail: String, val isDefault: Boolean = false )
2. 页面层状态托管
把选中状态提到LazyColumn的上层持有,初始值可以默认选中设置了默认标签的地址:
@Composable fun DeliveryAddressListPage() { // 实际项目中替换为从ViewModel/数据层获取的地址列表 val addressList = remember { mutableStateListOf( DeliveryAddress(id = "1", contactName = "张三", phone = "138****1234", detail = "北京市朝阳区建国路88号", isDefault = true), DeliveryAddress(id = "2", contactName = "李四", phone = "139****5678", detail = "上海市浦东新区陆家嘴环路1000号"), DeliveryAddress(id = "3", contactName = "王五", phone = "137****9012", detail = "广州市天河区珠江新城冼村路") ) } // 核心:只存储当前选中的地址ID,单一数据源保证单选逻辑 var selectedAddressId by remember { mutableStateOf(addressList.firstOrNull { it.isDefault }?.id) }
3. LazyColumn列表实现
渲染列表时给每个条目绑定选中判断逻辑,点击条目时直接更新全局存储的选中ID即可,旧选中项会自动取消选中:
LazyColumn( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { items( items = addressList, key = { it.id } // 必须设置key,用地址唯一ID作为key,避免重组时状态错乱 ) { address -> val isSelected = address.id == selectedAddressId AddressItem( address = address, isSelected = isSelected, onItemClick = { // 更新选中ID,直接覆盖之前的选中值 selectedAddressId = address.id // 后续可在这里加回调,把选中的地址传给ViewModel做后续业务处理 } ) } } }
4. 地址条目组件实现
根据传入的选中状态切换UI样式即可,组件本身不需要持有任何选中状态:
@Composable fun AddressItem( address: DeliveryAddress, isSelected: Boolean, onItemClick: () -> Unit ) { Card( modifier = Modifier .fillMaxWidth() .clickable { onItemClick() }, border = if (isSelected) { BorderStroke(2.dp, Color(0xFF1677FF)) } else { BorderStroke(1.dp, Color.LightGray) }, colors = CardDefaults.cardColors( containerColor = if (isSelected) Color(0xFFE6F4FF) else Color.White ) ) { Row( modifier = Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { Column(modifier = Modifier.weight(1f)) { Row(verticalAlignment = Alignment.CenterVertically) { Text( text = address.contactName, fontSize = 16.sp, fontWeight = FontWeight.Medium ) Spacer(modifier = Modifier.width(8.dp)) Text( text = address.phone, fontSize = 14.sp, color = Color.Gray ) if (address.isDefault) { Spacer(modifier = Modifier.width(8.dp)) Box( modifier = Modifier .background(Color(0xFFFFF7E6), RoundedCornerShape(4.dp)) .padding(horizontal = 4.dp, vertical = 2.dp) ) { Text( text = "默认", fontSize = 12.sp, color = Color(0xFFFF7D00) ) } } } Spacer(modifier = Modifier.height(8.dp)) Text( text = address.detail, fontSize = 14.sp, color = Color.DarkGray ) } if (isSelected) { Icon( imageVector = Icons.Default.CheckCircle, contentDescription = "已选中", tint = Color(0xFF1677FF) ) } } } }
避坑提示
- 不要把选中状态下沉到单个Item内部用
remember独立持有,LazyColumn的Item复用机制会导致状态错乱,出现多个条目同时选中的问题 - 如果页面需要支持横竖屏切换、进程重启后状态保留,建议把
selectedAddressId托管到ViewModel层,用StateFlow或SavedStateHandle存储,不要直接在Composable页面内用remember持有 - 如果业务需要支持「点击已选中项即可取消选择」,只要把点击逻辑改成
selectedAddressId = if (selectedAddressId == address.id) null else address.id即可
内容的提问来源于stack exchange,提问作者user18082994
相关产品推荐
相关产品推荐

