I've been working on form/field validation, and th...
# compose
v
I've been working on form/field validation, and this is what I've come up with. Is it an abomination? Full code in thread, but I feel like I've done something wrong by using
mutableStateOf
in this way...
Copy code
val totalCost by remember {
    mutableStateOf(Field(mutableStateOf("1.01"), validator = CurrencyValidator))
}
Copy code
// a wrapper containing this weird extra mutableState for the text which is input
class Field(
    var value: MutableState<String> = mutableStateOf(""),
    var valid: Boolean = true,
    val validator: FieldValidator? = null
) {
    var message: String = ""
    fun onFieldUpdate(newValue: String) {
        valid = validator?.validate(newValue) ?: true
        value.value = newValue
        message = if (!valid) {
            "Invalid"
        } else {
            ""
        }
    }
}

// in my screen
val totalCost by remember {
   mutableStateOf(Field(mutableStateOf("1.01"), validator = CurrencyValidator))
}
CurrencyTextField(
    modifier = Modifier.weight(1f),
    field = totalCost,
    value = totalCost.value.value,
    onValueChange = { totalCost.onFieldUpdate(it) },
    enabled = inputEnabled,
    label = R.string.screen_recordCharge_totalCost
)

// definition of CurrencyTextField
@Composable
fun CurrencyTextField(
    modifier: Modifier = Modifier,
    field: Field,
    value: String,
    enabled: Boolean = true,
    onValueChange: (String) -> Unit,
    @StringRes label: Int
) {
    OutlinedTextField(
        modifier = modifier,
        value = value,
        onValueChange = onValueChange,
        singleLine = true,
        keyboardOptions = KeyboardOptions(
            keyboardType = KeyboardType.Number,
            imeAction = ImeAction.Next
        ),
        isError = !field.valid,
        colors = TextFieldDefaults.outlinedTextFieldColors(textColor = md_theme_light_onSurface),
        enabled = enabled,
        label = { Text(stringResource(label)) },
        supportingText = { Text(field.message) }
    )
}
I'm reluctant to go any further with this until I know that it is a valid approach. New to Compose so I fear I could be doing something weird and wrong and costly. I certainly don't like lines like
value = totalCost.value.value
when declaring the
CurrencyTextField
.
b
First off, I don't think you need the outer mutable state because unless I am mistaken, you never replace field?
Copy code
val totalCost by remember {
   Field(mutableStateOf("1.01"), validator = CurrencyValidator)
}
There are a few other little things you could fix up but that's the main one. Instead of creating a mutableStateOf from outside you could probably have it as a property
Copy code
class Field(initialValue: Int, ...) {
   var fieldValue by mutableStateOf(initialValue)
}
Have a read of that doc page, you might find that using a viewmodel is easier rather than a state holder but both would work
v
Thank you. I suspect you are correct and the outer mutuableStateOf isn't required. I'll test it tonight. Glad to know that I'm not doing something crazy, as a solo dev it can be hard to get that "sanity check" you sometimes need. Any other feedback gratefully received.
OK, so I've made some changes and created a custom
remember
function as per the state holder documentation:
Copy code
@Composable
fun rememberFieldState(initialValue: String = "", validator: FieldValidator? = null) = remember {
    Field(mutableStateOf(initialValue), validator = validator)
}
Which is used like this:
Copy code
val totalCost = rememberFieldState(initialValue = "1.01", validator = CurrencyValidator)
It's working as intended. Hopefully that's a bit more idiomatic.
Validation work. Broke everything else. Back to the drawing board!