Search code examples
androidkotlinandroid-jetpack-composetransformationandroid-compose-textfield

Get the value from VisualTransformation Jetpack Compose TextField


How can I get the transformed value from VisualTransformation in Jetpack compose TextField? since the only thing that is changing is the visual text not the actual input/buffered text?

class CurrencyAmountInputVisualTransformation(
    private val currencySymbol: String,
    private val unbufferedValueChange: (String) -> Unit
) : VisualTransformation {

    private val symbols = DecimalFormat().decimalFormatSymbols
    private val numberOfDecimals: Int = 2

    override fun filter(text: AnnotatedString): TransformedText {

        val zero = symbols.zeroDigit

        val inputText = text.text

        val intPart = inputText
            .dropLast(numberOfDecimals)
            .reversed()
            .chunked(3)
            .joinToString(symbols.groupingSeparator.toString())
            .reversed()
            .ifEmpty {
                zero.toString()
            }

        val fractionPart = inputText.takeLast(numberOfDecimals).let {
            if (it.length != numberOfDecimals) {
                List(numberOfDecimals - it.length) {
                    zero
                }.joinToString("") + it
            } else {
                it
            }
        }

        val formattedNumber = intPart + symbols.decimalSeparator.toString() + fractionPart
        val value = inputText.dropLast(numberOfDecimals)

        unbufferedValueChange("$value.$fractionPart")

        val newText = AnnotatedString(
            text = "$currencySymbol $formattedNumber",
            spanStyles = text.spanStyles,
            paragraphStyles = text.paragraphStyles
        )

        ...

        return TransformedText(newText, ...)
    }

I'm getting duplicate re-composition because of this approach since I have 2 mutable state, one for the input and one for the value that I'm getting from a lambda callback I passed to the VisualTransformation.

@Composable
internal fun CurrencyField(
   
) {

    val pattern = remember { Regex("^\\d*\\.?\\d*\$") }
    var input by remember { mutableStateOf("") }
    var amountInput by remember { mutableStateOf(0.00) }
   

    OutlinedTextInputField(
        text = input,
        onTextChanged = {
            if (it.isEmpty() || it.matches(pattern)) {
                input = it
            }
        },
        keyboardType = KeyboardType.NumberPassword,
        visualTransformation = CurrencyAmountInputVisualTransformation("PHP") {
            amountInput = it.toDouble()
        }
    )
}

Output: Input screenshot

Is there any way I can get

232323.23

without using a callback in the VisualTransformation class?

Thanks.


Solution

  • You have to apply the same filter used by the VisualTransformation

        var text by remember { mutableStateOf("") }
        val visualTransformation = MyVisualTransformation()
    
        TextField(
            value = text,
            onValueChange = { text = it },
            visualTransformation = visualTransformation
    
        )
    
        val transformedText = remember(text, visualTransformation) {
            visualTransformation.filter(AnnotatedString(text))
        }.text.text