Hi, Does anyone know how to mask password characte...
# compose-android
k
Hi, Does anyone know how to mask password characters in a
SecureTextField
keyboard? I'm trying to copy data from another TextField to a
SecureTextField
, but the clipboard doesn't convert the text into masked special characters. This works fine in XML using:
<com.google.android.material.textfield.TextInputLayout>
Any suggestions?
You can see the problem in here.
MainActivity.kt
Copy code
import android.os.Bundle
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.activity.enableEdgeToEdge
    import androidx.compose.foundation.interaction.MutableInteractionSource
    import androidx.compose.foundation.layout.Column
    import androidx.compose.foundation.layout.fillMaxSize
    import androidx.compose.foundation.layout.fillMaxWidth
    import androidx.compose.foundation.layout.height
    import androidx.compose.foundation.layout.padding
    import androidx.compose.foundation.text.BasicTextField
    import androidx.compose.foundation.text.KeyboardOptions
    import androidx.compose.foundation.text.input.TextFieldLineLimits
    import androidx.compose.foundation.text.input.TextFieldState
    import androidx.compose.foundation.text.input.rememberTextFieldState
    import androidx.compose.material3.ExperimentalMaterial3Api
    import androidx.compose.material3.SecureTextField
    import androidx.compose.material3.Text
    import androidx.compose.material3.TextFieldDefaults
    import androidx.compose.runtime.Composable
    import androidx.compose.runtime.remember
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.text.input.ImeAction
    import androidx.compose.ui.text.input.KeyboardType
    import androidx.compose.ui.unit.dp
    
    class MainActivity : ComponentActivity() {
    
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            enableEdgeToEdge()
            setContent {
                val stateOne = rememberTextFieldState()
                val stateTwo = rememberTextFieldState()
                BasicTextFieldExamples(
                    stateOne,
                    remember { MutableInteractionSource() },
                    stateTwo,
                    remember { MutableInteractionSource() },
                )
            }
        }
    
        @OptIn(ExperimentalMaterial3Api::class)
        @Composable
        fun BasicTextFieldExamples(
            stateOne: TextFieldState,
            firstInteractionSource: MutableInteractionSource,
            stateOTwo: TextFieldState,
            secondInteractionSource: MutableInteractionSource,
        ) {
            Column(
                Modifier
                    .fillMaxSize()
                    .padding(50.dp)
            ) {
                BasicTextField(
                    state = stateOne,
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(56.dp),
                    keyboardOptions = KeyboardOptions(
                        keyboardType = KeyboardType.Email,
                        imeAction = ImeAction.Next
                    ),
                    interactionSource = firstInteractionSource,
                    decorator =
                    TextFieldDefaults.decorator(
                        state = stateOne,
                        enabled = true,
                        label = {
                            Text("Username")
                        },
                        placeholder = {
                            Text("Username Placeholder")
                        },
                        lineLimits = TextFieldLineLimits.Default,
                        interactionSource = firstInteractionSource,
                        outputTransformation = null
                    )
                )
                SecureTextField(
                    state = stateOTwo,
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(56.dp),
                    keyboardOptions = KeyboardOptions(
                        keyboardType = KeyboardType.Password,
                        imeAction = ImeAction.Done
                    ),
                    interactionSource = secondInteractionSource,
                    label = {
                        Text("Password")
                    },
                    placeholder = {
                        Text("Password Placeholder")
                    },
                )
            }
        }
 }
libs.versions.toml
Copy code
[versions]
    agp = "8.7.3"
    kotlin = "2.1.0"
    coreKtx = "1.15.0"
    junit = "4.13.2"
    junitVersion = "1.2.1"
    espressoCore = "3.6.1"
    lifecycleRuntimeKtx = "2.8.7"
    activityCompose = "1.10.0"
    composeBom = "2025.01.00"
    material3Release = "1.4.0-alpha06"
    
    [libraries]
    androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
    junit = { group = "junit", name = "junit", version.ref = "junit" }
    androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
    androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
    androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
    androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
    androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
    androidx-ui = { group = "androidx.compose.ui", name = "ui" }
    androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
    androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
    androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
    androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
    androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
    androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3Release" }
    
    [plugins]
    android-application = { id = "com.android.application", version.ref = "agp" }
    kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
    kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
z
Your code looks fine to me, please file a bug and post here. Also include android and gboard version since this could potentially be a gboard bug (it looks like you’re using gboard, if you’re not all bets are off)
v
here is the link https://issuetracker.google.com/issues/392542576. I'm using gboard `version 12.4.05.482060964-preload-arm64-v8`a
👍🏻 1
k
Have you tried copying from a
SecureTextField
to another
SecureTextField
? Do you have same issue?
k
no it's not happening when you do copying from one SecureTextField to another.
k
I think that might be expected as you are copying from a non secure textfield
keyboardType = KeyboardType.Email
thus clipboard doesn't deem the text as protected. You could probably try disabling copying to clipboard or provide more context on what you're trying to achieve there might be other alternatives.
☝🏼 1