android-jetpack-composetextfieldandroid-jetpackandroid-compose-textfield

Textfield Visualtransformation crashing iwth offsetmapping originaltotransformed returned invalid mapping


I have a textfield and trying to achieve something like

username textfield

I am trying to do this with visual transformation but running into issues. when I edit seems error like offsetmapping.originaltotransformed returned invalid mapping: Anyone knows how can I achieve this ?

class UserNameMaskTransformation() : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        return maskFilter(text)
    }
}


fun maskFilter(text: AnnotatedString): TransformedText {
    var maskLen = 4
    var maskChar = "*"

    val maskingChars = AnnotatedString(maskChar.repeat(maskLen))

    val subString = AnnotatedString(text.substring(4))
    return TransformedText(maskingChars + subString, OffsetMapping.Identity)
    
}

Solution

  • enter image description here

    You can use the onFocusChanged callback to determine the current focus and apply the relevant transformation.

    @Composable
    fun TextFieldMaskTest() {
        var hasFocus by remember {
            mutableStateOf(false)
        }
        var text by remember { mutableStateOf("johndoe") }
        Column {
            TextField(
                    modifier = Modifier
                            .onFocusChanged { hasFocus = it.isFocused },
                    value = text,
                    onValueChange = {
                        text = it
                    },
                    visualTransformation = if (hasFocus) {
                        VisualTransformation.None
                    } else {
                        MaskingTransformation()
                    }
            )
            TextField(value = "", onValueChange = {})
        }
    }
    

    Update your transformation class to handle the text length properly (as suggested by the @Abdo21)

    class MaskingTransformation : VisualTransformation {
        private val maskLen = 4
        private val maskChar = "*"
        override fun filter(text: AnnotatedString): TransformedText {
            val maskLengthToUse = minOf(maskLen, text.text.length)
            return TransformedText(
                    AnnotatedString(maskChar.repeat(maskLengthToUse) + text.text.substring(maskLengthToUse)),
                    OffsetMapping.Identity
            )
        }
    }