简体   繁体   中英

Access TextField value from another composable function in Jetpack Compose

I have created two TextField Email, Password & Button Login. Now on click of that button I want to access text and show success/error based on validation.

The problem is they are in two different composable functions.

@Composable
    fun EmailField() {
        var email by remember { mutableStateOf("") }

        TextField(
            modifier = Modifier.fillMaxWidth(0.9f),
            colors = TextFieldDefaults.textFieldColors(
                textColor = Color.White,
                focusedIndicatorColor = Color.White,
                focusedLabelColor = Color.White
            ),
            value = email,
            onValueChange = { email = it },
            label = { Text("Email") },
            leadingIcon = {
                Icon(
                    Icons.Filled.Email,
                    "contentDescription",
                    modifier = Modifier.clickable {})
            }
        )
    }

Button:

@Composable
    private fun LoginButton() {
        Button(
            onClick = {
                      // validate email and password here
            },
            colors = ButtonDefaults.buttonColors(
                backgroundColor = Color.Yellow,
                contentColor = Color.White
            )
        ) {
            Text(text = "Login")
        }
    }

If you want to see whole activity this is how it's structured at the moment.

class LoginActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            AppTheme {
                Column(
                    modifier = Modifier
                        .fillMaxSize()
                        .background(color = MaterialTheme.colors.primary),
                    verticalArrangement = Arrangement.Top,
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    Spacer(modifier = Modifier.height(32.dp))
                    LoginLogo()
                    Spacer(modifier = Modifier.height(32.dp))
                    Text(
                        text = "Login",
                        modifier = Modifier.fillMaxWidth(0.9f),
                        style = MaterialTheme.typography.h5,
                        textAlign = TextAlign.Start
                    )
                    Spacer(modifier = Modifier.height(12.dp))
                    Text(
                        text = "Please sign in to continue",
                        modifier = Modifier.fillMaxWidth(0.9f),
                        style = MaterialTheme.typography.subtitle1,
                        textAlign = TextAlign.Start
                    )
                    Spacer(modifier = Modifier.height(32.dp))
                    EmailField()
                    Spacer(modifier = Modifier.height(16.dp))
                    PassWordField()
                    Spacer(modifier = Modifier.height(16.dp))
                    LoginButton()
                }
            }
        }
    }

    @Composable
    private fun LoginButton() {
        Button(
            onClick = {
                      // validate email and password here
            },
            colors = ButtonDefaults.buttonColors(
                backgroundColor = Color.Yellow,
                contentColor = Color.White
            )
        ) {
            Text(text = "Login")
        }
    }

    @Composable
    fun LoginLogo() {
        Image(
            painter = painterResource(R.drawable.ic_vector_app_logo),
            contentDescription = "Login Logo",
            modifier = Modifier
                .width(120.dp)
                .height(120.dp)
        )
    }

    @Composable
    fun EmailField() {
        var email by remember { mutableStateOf("") }

        TextField(
            modifier = Modifier.fillMaxWidth(0.9f),
            colors = TextFieldDefaults.textFieldColors(
                textColor = Color.White,
                focusedIndicatorColor = Color.White,
                focusedLabelColor = Color.White
            ),
            value = email,
            onValueChange = { email = it },
            label = { Text("Email") },
            leadingIcon = {
                Icon(
                    Icons.Filled.Email,
                    "contentDescription",
                    modifier = Modifier.clickable {})
            }
        )
    }

    @Composable
    fun PassWordField() {
        var password by rememberSaveable { mutableStateOf("") }

        TextField(
            modifier = Modifier.fillMaxWidth(0.9f),
            colors = TextFieldDefaults.textFieldColors(
                textColor = Color.White,
                focusedIndicatorColor = Color.White,
                focusedLabelColor = Color.White
            ),
            value = password,
            onValueChange = { password = it },
            label = { Text("Password") },
            visualTransformation = PasswordVisualTransformation(),
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
            leadingIcon = {
                Icon(
                    Icons.Filled.Lock,
                    "contentDescription",
                    modifier = Modifier.clickable {})
            }
        )
    }


}

What is the right way to handle values in this case?

You can use a ViewModel or something like:

class TextFieldState(){
    var text: String by mutableStateOf("")
}

@Composable
fun login(){

    var emailState = remember { TextFieldState() }

    EmailField(emailState)
    LoginButton( onValidate = { /** validate **/})

}

with:

@Composable
fun EmailField( emailState : TextFieldState = remember { TextFieldState() }) {

    TextField(
        value = emailState.text,
        onValueChange = {
            emailState.text = it
        },
        //your code
   )
}

and:

@Composable
private fun LoginButton(onValidate: () -> Unit) {
    Button(
        onClick =  onValidate,
        //your code
    )
}

Here is another solution with rememberSaveable

@Composable
fun MainBody() {
Column(
    modifier = Modifier
        .fillMaxHeight()
        .fillMaxWidth()
) {
    var inputValue by rememberSaveable { mutableStateOf("") }
    CommonTextField(value = inputValue, onInputChanged = { inputValue = it })
}
}

And here it is defined CommonTextField Function

 @Composable
 fun CommonTextField(value: String, onInputChanged: (String) -> Unit) {
 TextField(value = value,
    onValueChange = onInputChanged,
 modifier = Modifier
    .fillMaxWidth()
    .padding(16.dp),
label = { Text(text = "Enter here")})
}

I have achieved this with the following way:-

@Composable
fun login(){
    var dataInput = "xyz@abc.com"
    EmailField(dataInput, onChange = { dataInput = it })
    LoginButton( onValidate = { /** validate **/})
}

@Composable
fun EmailField(textData: String, onChange: (String) -> Unit = {}) {
   var textInput by remember { mutableStateOf(textData) }    
   TextField(
       value = textInput,
       onValueChange = {
          textInput = it
          onChange(it)
       },
                //your code
     )
}

The beauty of this is that it will not recompose all the other views again on changing input for any TextField.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM