读取非Compose状态时Jetpack Compose输入框被清空的问题
Jetpack Compose表单输入内容被重置的问题修复
问题描述
基于教程实现的Jetpack Compose表单,提交验证功能正常,但给按钮添加enabled = state.requiredFieldsNotEmpty()动态启用逻辑后,每次调用该方法时输入框内容会被瞬间清空,尤其执行field.text.length < it.length时触发Bug。
核心原因
- Field实例不稳定:父Composable中每次重组都会创建新的
Field列表,Form组件又将其赋值给state.fields,导致原Field实例被替换,新实例的text初始值为空。 - Validator实例判断错误:
field.validators.contains(Required())会创建新的Required实例,未重写equals导致判断永远为false,同时引发不必要的对象创建和重组。 - 重组触发逻辑不当:按钮
enabled属性每次重组都会调用requiredFieldsNotEmpty(),若此时Field实例已被替换,就会读取新实例的空text并刷新UI。
解决方案
1. 固定Field实例,避免重复创建
在父Composable中用remember保存Field列表,确保实例稳定:
// 父组件中 val fields = remember { listOf( Field( name = "pan", placeholderHint = R.string.card_number, validators = listOf(Required(), MinLength(length = Constants.MINIMUM_PAN_LENGTH)) ) ) } val state by remember { mutableStateOf(FormState(fields)) }
2. 重构FormState与Form组件,禁止随意替换Fields
修改FormState,初始化时传入fields并禁止外部修改:
class FormState(initialFields: List<Field>) { val fields: List<Field> = initialFields // 改为val,禁止外部修改 fun validate(): Boolean { var valid = true fields.forEach { if (!it.validate()) valid = false } return valid } fun requiredFieldsNotEmpty(): Boolean { fields.forEach { field -> // 改用类型判断是否包含Required验证器 val hasRequired = field.validators.any { it is Required } if (hasRequired && field.text.isBlank()) return false val minLength = field.validators.find { it is MinLength } as MinLength? minLength?.let { if (field.text.length < it.length) return false } } return true } fun getData(): Map<String, String> = fields.associate { it.name to it.text } }
修改Form组件,直接使用state中的fields:
@Composable fun Form( modifier: Modifier = Modifier, state: FormState ) { Column(modifier = modifier.padding(horizontal = 16.dp)) { state.fields.forEach { it.Content(modifier) } } }
3. 修复Validator的判断逻辑
移除contains(Required())的实例判断,改用类型检查(如上述requiredFieldsNotEmpty()中的代码),若需要自定义message的Validator,可重写equals和hashCode:
open class Required(var message: Int = requiredMessage) : Validator { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as Required return message == other.message } override fun hashCode(): Int = message } open class MinLength(val length: Int, var message: Int = minLengthMessage): Validator { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as MinLength return length == other.length && message == other.message } override fun hashCode(): Int = 31 * length + message }
4. 优化按钮触发逻辑
确保按钮的enabled状态能响应Field文本变化:
TextButton( onClick = { if (state.validate()) submitData(state.getData()) }, enabled = state.requiredFieldsNotEmpty() ) { Text(text = stringResource(id = R.string.confirm)) }
修改后效果
输入框内容不再被重置,按钮会根据输入内容动态启用/禁用,验证逻辑保持正常。
内容的提问来源于stack exchange,提问作者Daniel Wilson
相关产品推荐
相关产品推荐

