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

Jetpack Compose动态表单状态丢失与数据不全问题求助

问题分析与解决方案

你的两个核心问题根源在于状态未全局持久化:

  • 切换步骤时会重新调用breakdownStepFields生成新的TextFieldState实例,导致之前的输入丢失
  • data变量仅保存当前步骤的字段状态,点击Finish时无法获取其他步骤的数据

以下是针对性修复方案:


1. 全局持久化所有字段状态

在主界面维护一个全局Map,存储所有步骤中所有字段的TextFieldState,确保切换步骤时状态不会被重置:

@SuppressLint("UnrememberedMutableState")
@Preview
@Composable
fun LoanFormScreen(navController: NavController = rememberNavController()) {
    val api = API
    val loanForm: MutableState<LoanForm?> = remember { mutableStateOf(null) }
    val coroutineScope = rememberCoroutineScope()
    val currentStep = rememberSaveable { mutableStateOf(0) }
    // 全局存储所有字段的状态
    val allFieldStates = remember { mutableMapOf<String, TextFieldState>() }

    MainScaffold(
        title = "",
        isDefaultColumn = false,
        content = {
            loanForm.value?.let { form ->
                val currentStepData = form.successNotification.steps.getOrNull(currentStep.value)
                val stepFieldsList = currentStepData?.values?.firstOrNull()?.fields ?: listOf()
                val data = breakdownStepFields(stepFieldsList, allFieldStates)
                LoanFormContent(currentStep, data, allFieldStates, form.successNotification.steps.size)
            } ?: run {
                Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                    CircularProgressIndicator()
                }
            }
        },
        navController = navController
    )

    SideEffect {
        coroutineScope.launch {
            loanForm.value = api.loadLoanApplicationForm()
        }
    }
}

2. 修改字段构建逻辑,复用已有状态

更新breakdownStepFields函数,优先使用全局Map中已存在的状态,避免重复创建:

@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun breakdownStepFields(
    stepFieldsList: List<Field>,
    allFieldStates: MutableMap<String, TextFieldState>
): MutableMap<String, Pair<TextFieldState, @Composable () -> Unit>> {
    val textFieldStatesMap = mutableMapOf<String, Pair<TextFieldState, @Composable () -> Unit>>()

    stepFieldsList.forEach { field ->
        val key = field.key ?: return@forEach
        // 复用已有状态,不存在则创建新状态
        val state = allFieldStates.getOrPut(key) {
            TextFieldState(
                name = field.label?.en.toString(),
                validators = if (field.type?.contains("radio") == true || field.type?.contains("dropdown") == true) listOf()
                else listOf(
                    Validators.Custom(message = field.regex?.err?.en.toString(), function = {
                        it.toString().matches(Regex(field.regex?.value ?: ""))
                    })
                )
            )
        }

        val dependentFieldState = allFieldStates[field.dependentOn]
        val isDisabled =
            dependentFieldState != null && field.dependentValueRequired == true && (dependentFieldState.value.isEmpty() || !field.dependencyValues?.contains(
                dependentFieldState.value
            )!!)

        val textFieldBuilder: @Composable () -> Unit = {
            CustomField(
                label = field.label?.en ?: "",
                hint = field.hint?.en ?: "",
                state = state,
                onValueChange = { state.change(it) },
                isNumberInput = field.type?.contains("number") ?: false,
                isDropDownField = field.type?.contains("dropdown") ?: false,
                dropDownMenuItems = field.dropdownItems?.map { item -> item.value ?: "" }?.toList() ?: listOf(),
                isRadioBox = field.type?.contains("radio") ?: false,
                radioButtonItems = field.options?.map { option -> option.value ?: "" }?.toList() ?: listOf(),
                isFile = field.type?.contains("file") ?: false,
                isDisabled = isDisabled,
            )
        }

        textFieldStatesMap[key] = state to textFieldBuilder
    }

    return textFieldStatesMap
}

3. 调整Finish逻辑,收集所有步骤数据

修改LoanFormContent,传入全局状态Map,点击Finish时遍历所有字段状态:

@SuppressLint("UnrememberedMutableState")
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun LoanFormContent(
    currentStep: MutableState<Int>,
    data: MutableMap<String, Pair<TextFieldState, @Composable () -> Unit>>,
    allFieldStates: MutableMap<String, TextFieldState>,
    totalSteps: Int
) {

    Column(
        modifier = Modifier
            .background(mainSurfaceColor)
            .padding(horizontal = 15.dp),
    ) {
        Card(
            backgroundColor = Color.White,
            shape = RoundedCornerShape(15.dp)
        ) {
            Text(
                text = stringResource(id = R.string.loan_application_form),
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(vertical = 25.dp),
                style = MaterialTheme.typography.h5,
                textAlign = TextAlign.Center
            )
        }

        Spacer(modifier = Modifier.height(16.dp))

        Card(
            backgroundColor = Color.White,
            shape = RoundedCornerShape(15.dp)
        ) {
            LazyColumn(
                modifier = Modifier
                    .padding(15.dp),
                verticalArrangement = Arrangement.spacedBy(16.dp)
            ) {
                data.forEach { (key, value) ->
                    item {
                        value.second()
                    }
                }
                item {
                    Row(Modifier.fillMaxWidth()) {
                        if (currentStep.value > 0) {
                            CustomButton(
                                text = "Previous",
                                onClick = {
                                    currentStep.value--
                                },
                                modifier = Modifier.weight(1f)
                            )
                            Spacer(modifier = Modifier.width(2.dp))
                        }
                        CustomButton(
                            text = if (currentStep.value == totalSteps - 1) "Finish" else "Next",
                            onClick = {
                                val currentStepData =
                                    data.map { it.value.first.validate() }
                                if (currentStepData.all { it }) {
                                    if (currentStep.value == totalSteps - 1) {
                                        val jsonFormData = mutableMapOf<String, String>()
                                        // 遍历全局状态Map获取所有字段数据
                                        allFieldStates.forEach { (key, state) ->
                                            jsonFormData[key] = state.value
                                        }
                                        Log.e("json", jsonFormData.toString())
                                    } else {
                                        currentStep.value++
                                    }
                                }
                            },
                            modifier = Modifier.weight(1f)
                        )
                    }
                }
            }
        }
        Spacer(Modifier.height(16.dp))
    }
}

核心修复点说明

  • 状态持久化:全局allFieldStatesMap保存所有字段的状态,切换步骤时不会丢失输入
  • 状态复用:使用getOrPut确保每个字段只创建一次TextFieldState
  • 全量数据收集:Finish时遍历全局状态Map,获取所有步骤的字段值

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 06:32:16