我有一个Jetpack compose表单,从this tutorial创建。它通过在您单击提交按钮时验证表单来很好地显示错误。我希望表单上的按钮能够使用相同的表单状态动态启用。
问题是state.requiredFieldsNotEmpty()
似乎在每次调用可变状态文本变量时都会重置它。这意味着打字什么也不做,因为它立即清除。为什么?它所做的只是读取状态变量,没有写入。专门调用这一行field.text.length < it.length
会触发bug并清除field.text。
相关编码:
val state by remember {
mutableStateOf(FormState())
}
Column {
Form(
state = state,
fields = listOf(
Field(name = "pan",
placeholderHint = R.string.card_number,
validators = listOf(Required(), MinLength(length = Constants.MINIMUM_PAN_LENGTH))),
)
)
Row {
TextButton(
onClick = { if (state.validate()) submitData(state.getData()) },
enabled = state.requiredFieldsNotEmpty(),
) {
Text(
text = stringResource(id = R.string.confirm),
)
}
}
}
Form.kt:
@Composable
fun Form(
modifier: Modifier,
state: FormState,
fields: List<Field>) {
state.fields = fields
Column(modifier = modifier.padding(horizontal = 16.dp)) {
fields.forEach {
it.Content(modifier)
}
}
}
class FormState {
var fields: List<Field> = listOf()
set(value) {
field = value
}
fun validate(): Boolean {
var valid = true
for (field in fields) if (!field.validate()) {
valid = false
break
}
return valid
}
fun requiredFieldsNotEmpty(): Boolean {
for (field in fields){
if(field.validators.contains(Required()) && 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.map { it.name to it.text }.toMap()
}
Validators.kt
sealed interface Validator
open class Email(var message: Int = emailMessage) : Validator
open class NotExpired(var message: Int = expiryMessage) : Validator
open class Required(var message: Int = requiredMessage) : Validator
open class MinLength(val length: Int, var message: Int = minLengthMessage): Validator
Field.kt
class Field(
val name: String,
val placeholderHint: Int = R.string.empty,
val error: Int = R.string.empty,
val singleLine: Boolean = true,
val keyboardOptions: KeyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
val validators: List<Validator>
) {
var text: String by mutableStateOf("")
var supportingError: Int by mutableStateOf(error)
var minLength: Int by mutableStateOf(-1)
var hasError: Boolean by mutableStateOf(false)
private fun showError(error: Int) {
hasError = true
supportingError = error
}
private fun showMinLengthError(error: Int, length: Int) {
hasError = true
minLength = length
supportingError = error
}
private fun hideError() {
supportingError = error
minLength = -1
hasError = false
}
@Composable
fun Content(
modifier: Modifier,
) {
OutlinedTextField(value = text, isError = hasError, supportingText = {
Text(text = stringResource(id = supportingError))
}, singleLine = singleLine, keyboardOptions = keyboardOptions, placeholder = {
Text(
text = stringResource(id = placeholderHint),
)
}, modifier = modifier
.fillMaxWidth()
.padding(10.dp), onValueChange = { value ->
hideError()
text = value
})
}
fun validate(): Boolean {
return validators.map {
when (it) {
is Email -> {
if (!Patterns.EMAIL_ADDRESS.matcher(text).matches()) {
showError(it.message)
return@map false
}
true
}
is Required -> {
if (text.isEmpty()) {
showError(it.message)
return@map false
}
true
}
is NotExpired -> {
val month = text.substring(0, 2).trimStart('0').toInt()
val year = text.substring(2, 4).trimStart('0').toInt()
val now = LocalDate.now()
if ((year < now.year) || (year == now.year && month < now.monthValue)) {
showError(it.message)
return@map false
}
true
}
is MinLength -> {
if (text.length < it.length) {
showMinLengthError(it.message, it.length)
return@map false
}
true
}
}
}.all { it }
}
}
2条答案
按热度按时间erhoui1w1#
我发现原始代码存在以下问题:
1.表单字段本身永远不会被记住(因此嵌套的状态属性将在每次呈现时被新属性替换)
Required
类具有引用语义,因此field.validators.contains(Required())
永远不会匹配requiredFieldsNotEmpty()
的计算依赖于状态(由于声明var text: String by mutableStateOf("")
),但没有 Package 在derivedStateOf
中以下调整:
解决了这个问题。
oalqel3c2#
我尝试了提供的代码,我发现从UI中提取状态是最佳实践。
在提供的代码中,FormComposable函数遵循了从UI中提取状态的最佳实践。Form类负责维护表单字段的状态,并处理表单字段的验证。UI是使用JetpackCompose可组合函数组合的,这些函数使用Form类提供的状态。
FormField函数是一个可组合的函数,用于表示用户界面中的表单字段。以FormFieldState类型的formFieldState对象和onValueChange回调函数为参数
Form类表示表单字段的集合,提供处理和验证表单数据的功能。
Form和FormFieldState的一个示例用法。