You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose中用户输入无法显示在LazyList的问题求助

问题:Jetpack Compose中ModalBottomSheet添加的任务无法显示在LazyList

通过ModalBottomSheet输入的任务保存后,无法在LazyList中显示,怀疑和User类、testUser的设置以及状态管理有关。


User类代码

class User (
    var name: String,
    val email: String
    )
{
    val currentTasks : MutableList<Task> = ArrayList()
    val archivedTasks: MutableList<Task> = ArrayList()
    private var currentPoints: Int = 0
    private var lifetimePoints: Int = 0
    private var nextTaskID: Int = 0
    var categories: MutableList<Category> = ArrayList()


    fun startUp(){
        addCategory(Category("Default"))
        addCategory(Category("Chores"))
        addCategory(Category("Home"))
        addCategory(Category("Work"))
        addTask(Task("Task 1", categories[0], "High", "Hard", false))
        addTask(Task("Task 2", categories[1], "Medium", "Medium", false))
        addTask(Task("Task 3", categories[2], "Low", "Hard", false))
        addTask(Task("Task 4", categories[0], "Medium", "Easy", false))
    }


    fun getLifetimePoints(): Int {
        return lifetimePoints
    }

    fun getCurrentPoints(): Int {
        return currentPoints
    }

    fun numCurrentTasks(): Int {
        return currentTasks.size
    }

    fun numTotalTasks(): Int {
        return currentTasks.size + archivedTasks.size
    }

    fun getNextTaskID(): Int {
        return nextTaskID
    }
    fun addTask(task: Task){
        currentTasks.add(task)
        //to do: categories solution

    }

    fun archiveTask(task: Task){
        currentTasks.remove(task)
        archivedTasks.add(task)
    }


    fun deleteTask(task: Task){
        currentTasks.remove(task)
    }

    private fun minusCurrPoints(points: Int){
        currentPoints -= points
    }
    fun minusPoints(points: Int){
        currentPoints -= points
        lifetimePoints -= points
    }

    fun addPoints(points: Int){
        currentPoints += points
        lifetimePoints += points
    }


    fun redeemPrize(prize: Prize){
        minusCurrPoints(prize.cost)
    }

    fun addCategory(cat: Category){
        if (categories.contains(cat)){
           return
        }
        val newMap = HashMap<String, Task>()
        categories.add(cat)
    }

    private fun removeCategory(cat: Category){
        if (!categories.contains(cat)){
            return
        }
        categories.remove(cat)
    }
}

主界面及TestUser代码

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun TaskScreen(contentPadding: PaddingValues = PaddingValues()) {

    val testUser = User("jason", "jason@hotmail.com")
    testUser.startUp()
    var categories = testUser.categories
    var tasks = testUser.currentTasks



    // 跟踪分类下拉菜单是否展开的状态
    var expanded by remember { mutableStateOf(false) }
    var showTaskBottomSheet by remember { mutableStateOf(false) }



    // 跟踪分类开关状态
    val categoryCheckedState = remember {
        mutableStateMapOf<Category, Boolean>().apply {
            categories.forEach { category ->
                this[category] = true
            }
        }
    }

    // 跟踪任务复选框选中状态
    val checkboxCheckedState = remember {
        mutableStateMapOf<Task, Boolean>().apply {
            tasks.forEach { task ->
                this[task] = task.checked
            }
        }
    }

    // 跟踪选中的分类
    var selectedCategories by remember { mutableStateOf(emptyList<Category>()) }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(contentPadding)
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp),
            horizontalArrangement = Arrangement.SpaceAround
        ) {
            // 带下拉菜单的分类按钮
            Box {
                ElevatedButton(onClick = { expanded = true }) {
                    Text("Category")
                }
                CustomDropdownMenu(
                    expanded = expanded,
                    onExpandedChange = { expanded = it },
                    categories = categories,
                    categoryCheckedState = categoryCheckedState,
                    onCategoryClick = { category ->
                        val isChecked = categoryCheckedState[category] ?: false
                        categoryCheckedState[category] = !isChecked
                        selectedCategories = if (isChecked) {
                            selectedCategories - category
                        } else {
                            selectedCategories + category
                        }
                    }
                )
            }
            // 优先级按钮 - 需实现排序功能
            ElevatedButton(
                onClick = { /* 处理按钮点击 */ }
            ) {
                Text("Priority")
            }
            // 难度按钮 - 需实现排序功能
            ElevatedButton(onClick = { /* 处理按钮点击 */ }) {
                Text("Difficulty")
            }
        }
        Box(modifier = Modifier.fillMaxSize()) {
            LazyColumn(
                //modifier = Modifier.fillMaxSize(),
                contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp)
            ) {
                val groupedTasks = tasks.groupBy { it.category }
                // 显示未关闭的分类
                categories.forEach { category ->
                    if (categoryCheckedState[category] == true) {
                        // 分类粘性头部
                        stickyHeader {
                            Box(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .background(
                                        Color.LightGray.copy(alpha = 0.4f),
                                        shape = RoundedCornerShape(4.dp)
                                    )
                                    .padding(5.dp)
                            ) {
                                Text(
                                    text = category.name,
                                    modifier = Modifier.padding(8.dp)
                                )
                            }
                        }
                        // 分类下的任务列表
                        items(groupedTasks[category] ?: emptyList()) { task ->
                            Box(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .padding(vertical = 3.dp)
                                    .border(
                                        1.dp,
                                        Color.Gray.copy(alpha = 0.4f),
                                        shape = RoundedCornerShape(4.dp)
                                    )
                            ) {
                                Row(
                                    modifier = Modifier
                                        .padding(vertical = 8.dp),
                                    verticalAlignment = Alignment.CenterVertically,
                                ) {
                                    // 按难度配色的任务复选框
                                    Checkbox(
                                        checked = checkboxCheckedState[task] ?: false,
                                        onCheckedChange = { isChecked ->
                                            checkboxCheckedState[task] = isChecked
                                            task.checked = isChecked
                                        },
                                        colors = CheckboxDefaults.colors(
                                            uncheckedColor = getDifficultyColor(task.difficulty),
                                            checkedColor = getDifficultyColor(task.difficulty)
                                        )
                                    )
                                    // 任务名称
                                    Text(
                                        text = task.name,
                                        modifier = Modifier.padding(start = 1.dp)
                                    )
                                    // 优先级图标 - 需优化显示方式避免界面拥挤
//                                repeat(getPriorityIconCount(task.priority)) {
//                                    Icon(
//                                        imageVector = Icons.Outlined.PriorityHigh,
//                                        contentDescription = "Priority High",
//                                        modifier = Modifier
//                                            .padding(start = 0.dp, end = 0.dp)
//                                    )
//                                }
                                }
                            }
                        }
                    }
                }
            }
            // 添加任务的自定义浮动按钮
            Box(
                contentAlignment = Alignment.BottomEnd,
                modifier = Modifier
                    .fillMaxSize()
                    .padding(16.dp)
            ) {
                TaskFloatingActionButton(
                    onClick = { showTaskBottomSheet = true }
                )
            }
        }

        if (showTaskBottomSheet) {
            TaskBottomSheet(
                padding = contentPadding,
                showTaskBottomSheet = showTaskBottomSheet,
                categories = categories,
                onClose = { showTaskBottomSheet = false },
                onTaskSave = { newTask ->
                    testUser.addTask(newTask)
                    showTaskBottomSheet = false
                    Log.d("NewTask", "Name: ${newTask.name}, Category: ${newTask.category}, Priority: ${newTask.priority}, Difficulty: ${newTask.difficulty}")
                }
            )
        }
    }
}

底部弹窗代码

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TaskBottomSheet(
    padding: PaddingValues = PaddingValues(),
    showTaskBottomSheet: Boolean,
    categories: List<Category>,
    onClose: () -> Unit,
    onTaskSave: (Task) -> Unit
) {
    var isDifficultyMenuExpanded by remember { mutableStateOf(false) }
    var isPriorityMenuExpanded by remember { mutableStateOf(false) }
    var isCategoryMenuExpanded by remember { mutableStateOf(false) }

    val difficultyOptions = listOf("Low", "Medium", "High")
    var selectedDifficulty by remember { mutableStateOf("") }
    val priorityOptions = listOf("Low", "Medium", "High")
    var selectedPriority by remember { mutableStateOf("") }
    var selectedCategory by remember { mutableStateOf(Category("")) }
    var taskName by remember { mutableStateOf("") }

    if (showTaskBottomSheet) {
        ModalBottomSheet(
            onDismissRequest = onClose,
            sheetState = rememberModalBottomSheetState()
        ) {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(padding)
            ) {
                Column(
                    modifier = Modifier
                        .padding(15.dp)
                    //.fillMaxSize()
                ) {
                    // 用户输入框
                    TextField(
                        value = taskName,
                        onValueChange = { taskName = it },
                        label = { Text("Input new task here") }
                    )
                    Spacer(modifier = Modifier.height(40.dp))

                    // 分类按钮
                    Row(
                        modifier = Modifier
                            .fillMaxWidth(),
                        //  .padding(16.dp),
                        horizontalArrangement = Arrangement.SpaceAround
                    ) {
                        Column {

                            Box(
                                modifier = Modifier
                                    .offset(y = (-10).dp)
                                    .padding(bottom = 40.dp)
                            ) {
                                ElevatedButton(onClick = { isCategoryMenuExpanded = true }) {
                                    Text(selectedCategory.name.ifBlank { "Category" })
                                }
                            }

                            DropdownMenu(
                                expanded = isCategoryMenuExpanded,
                                onDismissRequest = { isCategoryMenuExpanded = false },
                                modifier = Modifier.requiredSizeIn(maxHeight = 160.dp)
                            ) {
                                categories.forEach { category ->
                                    DropdownMenuItem(
                                        text = { Text(category.name) },
                                        onClick = {
                                            selectedCategory = category
                                            isCategoryMenuExpanded = false
                                        }
                                    )
                                }
                            }
                        }


                        // 难度按钮
                        Column {
                            Box(
                                modifier = Modifier
                                    .offset(y = (-10).dp)
                                    .padding(bottom = 40.dp)
                            ) {
                                ElevatedButton(onClick = { isDifficultyMenuExpanded = true }) {
                                    Text(selectedDifficulty.ifBlank { "Difficulty" })
                                }
                            }
                            DropdownMenu(
                                expanded = isDifficultyMenuExpanded,
                                onDismissRequest = { isDifficultyMenuExpanded = false }
                            ) {
                                difficultyOptions.forEach { option ->
                                    DropdownMenuItem(
                                        text = { Text(option) },
                                        onClick = {
                                            selectedDifficulty = option
                                            isDifficultyMenuExpanded = false
                                        }
                                    )
                                }
                            }
                        }


                        // 优先级按钮
                        Column {
                            Box(
                                modifier = Modifier
                                    .offset(y = (-10).dp)
                                    .padding(bottom = 40.dp)
                            ) {
                                ElevatedButton(onClick = { isPriorityMenuExpanded = true }) {
                                    Text(selectedPriority.ifBlank { "Priority" })
                                }
                            }
                            DropdownMenu(
                                expanded = isPriorityMenuExpanded,
                                onDismissRequest = { isPriorityMenuExpanded = false }
                            ) {
                                priorityOptions.forEach { option ->
                                    DropdownMenuItem(
                                        text = { Text(option) },
                                        onClick = {
                                            selectedPriority = option
                                            isPriorityMenuExpanded = false
                                        }
                                    )
                                }
                            }
                        }
                    }


                    Spacer(modifier = Modifier.height(100.dp))
                    // 右下角保存图标
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.End
                    ) {
                        Box(
                            modifier = Modifier
                                .size(40.dp)
                                .clickable {
                                    val newTask = Task(
                                        name = taskName,
                                        category = selectedCategory,
                                        priority = selectedPriority,
                                        difficulty = selectedDifficulty,
                                        checked = false
                                    )
                                    onTaskSave(newTask)
                                    onClose()
                                }
                        ) {
                            Icon(
                                imageVector = Icons.Filled.SaveAs,
                                contentDescription = "Input Button to save new task",
                                modifier = Modifier.size(40.dp)
                            )
                        }
                    }
                }
            }
        }
    }
}

问题根源与修复方案

1. 核心问题

  • 普通列表无法触发Compose重组:User类中的currentTasks是普通ArrayList,添加任务后Compose无法感知到数据变化,因此不会更新LazyList。
  • testUser被重复初始化:每次TaskScreen重组时,都会重新创建testUser并调用startUp(),导致之前添加的任务全部丢失。

2. 具体修复步骤

步骤1:将User类中的列表改为可观察状态

用Compose提供的mutableStateListOf替代ArrayList,这样列表的增删改操作会自动通知Compose更新UI:

class User (
    var name: String,
    val email: String
) {
    val currentTasks: MutableList<Task> = mutableStateListOf()
    val archivedTasks: MutableList<Task> = mutableStateListOf()
    var categories: MutableList<Category> = mutableStateListOf()
    
    // 其余代码保持不变
}

步骤2:用remember持久化testUser

在TaskScreen中,用remember包裹testUser,确保组件重组时不会重新创建实例:

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun TaskScreen(contentPadding: PaddingValues = PaddingValues()) {
    val testUser = remember {
        User("jason", "jason@hotmail.com").apply {
            startUp()
        }
    }
    
    // 移除原有的var categories = testUser.categories和var tasks = testUser.currentTasks,直接使用testUser的属性
    // 其余代码保持不变
}

步骤3:更新LazyList的数据源引用

在LazyColumn中直接使用testUser.currentTasks,确保每次都读取最新的任务列表:

LazyColumn(
    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp)
) {
    val groupedTasks = testUser.currentTasks.groupBy { it.category }
    testUser.categories.forEach { category ->
        if (categoryCheckedState[category] == true) {
            // 粘性头部代码不变
            items(groupedTasks[category] ?: emptyList()) { task ->
                // 任务项代码不变
            }
        }
    }
}

步骤4:同步任务复选框状态(可选)

添加新任务后,手动将任务加入复选框状态映射,确保新任务的复选框状态正确:

onTaskSave = { newTask ->
    testUser.addTask(newTask)
    checkboxCheckedState[newTask] = newTask.checked
    showTaskBottomSheet = false
    Log.d("NewTask", "Name: ${newTask.name}, Category: ${newTask.category}, Priority: ${newTask.priority}, Difficulty: ${newTask.difficulty}")
}

内容的提问来源于stack exchange,提问作者Lizzie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 12:37:00