0

How can I properly handle number in text component in jetpack compose (with MVVM pattern)

Please note that the price can be null or have a value (maybe 0 btw)

I have a poor implementation for now, I changed the keyboard like this :

        OutlinedTextField(
            value = if (vm.viewState.collectAsState().value.price != null) vm.viewState.collectAsState().value.price.toString() else "",
            onValueChange = { vm.onProductPriceChange(it) },
            label = { Text(stringResource(id = R.string.price)) },
            keyboardOptions = KeyboardOptions(
                capitalization = KeyboardCapitalization.None,
                autoCorrect = true,
                keyboardType = KeyboardType.Number
            ),
        )

and for onValueChange :

fun onProductPriceChange(it: Any) {
    if (it.toString() == "") {
        _viewState.value = _viewState.value.copy(price = null)
    } else {
        try
        {
            _viewState.value = _viewState.value.copy(price = it.toString().toDouble())
        }
        catch (e: NumberFormatException)
        { // dismiss the bad entries
        }
    }
}

there can be multiple bad output of the user for example write 22..0 (I dismissed them which is a workaround acceptable)

but there are bad behaviour, when you want to write 10, it will convert it to 10.0. it is not huge but it has backwards

when you delete number in the EditText, 10.0 will become 10.0 and then 100.0 and then 10.0 and finally 1.0. btw it is impossible to go back to the null value (for this case, I can consider 0.0 = no value)

10.0 bug

I saw that VisualTransformation (https://medium.com/google-developer-experts/hands-on-jetpack-compose-visualtransformation-to-create-a-phone-number-formatter-99b0347fc4f6) could handle my case but the documentation seems complicated

 class DoubleVisualTransformation : VisualTransformation {
    override fun filter(str: AnnotatedString): TransformedText {
        val strNullDouble = str.text.toBigDecimalOrNull()

        var transformedString: String
        if (str.text == "" || strNullDouble == null)
            return TransformedText(AnnotatedString(""), OffsetMapping.Identity)
        else if (strNullDouble.toDouble() % 1 == 0.0 && str.text.last() != '.')
            transformedString = strNullDouble.toInt().toString()
        else
            transformedString = str.text


        return TransformedText(
            text = AnnotatedString(transformedString),
            offsetMapping = object : OffsetMapping {
                override fun originalToTransformed(offset: Int): Int {
                    return offset
                }

                override fun transformedToOriginal(offset: Int): Int {
                    return offset
                }
            }
        )
    }
}

how can I improve the behavior ?

P. Sohm
  • 2,842
  • 2
  • 44
  • 77

1 Answers1

0

What about not returning a double to your TextField but just the String?

fun onProductPriceChange(it: String) {
    if (it == "") {
        _viewState.value = _viewState.value.copy(price = null)
    } else {
        if (it.toDoubleOrNull() != null) {
            _viewState.value = _viewState.value.copy(price = it)
        }
    }
}
F.G.
  • 216
  • 5
  • well it seems like a old way to work. I would prefer there would be something more sexy like a numbercomponent – P. Sohm Oct 30 '22 at 16:42