Search code examples
androidandroid-jetpack-composecompose-recomposition

Composable doesn't re-compose on button click


I have the following composable function. Its a simple login screen with username, password and submit button.

If username or password fields are empty when the user clicks on the submit button a snackbar will show. However, it works on the first click, but subsequent clicks don't work.

I would think that the button click will force the composable to re-compose and the LaunchEffect key will be true if the snackbar needs to be shown.

@Composable
fun Login() {
    var username by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }
    val scaffoldState = rememberScaffoldState()
    var shouldShowSnackBar by remember { mutableStateOf(false) }

    println("LoginScreen")

    LaunchedEffect(key1 = shouldShowSnackBar, block = {
        println("ShouldShowSnackBar")
        scaffoldState.snackbarHostState.showSnackbar(
            message = "Please enter a username and password",
            duration = SnackbarDuration.Short
        )
    })

   Scaffold(
        scaffoldState = scaffoldState
    ) {
        Column(modifier = Modifier.fillMaxWidth()) {
            OutlinedTextField(
                label = {
                    Text(text = "Username")
                },
                value = username,
                onValueChange = { newUsername ->
                    username = newUsername
                })

            OutlinedTextField(
                label = {
                    Text(text = "Password")
                },
                value = password,
                onValueChange = { newPassword ->
                    password = newPassword
                },
                visualTransformation = PasswordVisualTransformation())

            OutlinedButton(
                modifier = Modifier.fillMaxWidth(),
                onClick = {
                    shouldShowSnackBar = username.isEmpty() || password.isEmpty()
                    println("Button pressed: $shouldShowSnackBar")
                }) {
                Text(text = "Submit")
            }
        }
    }
}

Solution

  • What you see is expected behavior. In jetpack Compose for recomposition to be triggered the State/MutableState should have new value and composition gets triggered in the scope State/MutableState is read only.

    You need to set shouldShowSnackBar to false and invoke block inside LaunchedEffect when shouldShowSnackBar is true.

    LaunchedEffect(key1 = shouldShowSnackBar, block = {
        if(shouldShowSnackBar){
            println("ShouldShowSnackBar")
            scaffoldState.snackbarHostState.showSnackbar(
                message = "Please enter a username and password",
                duration = SnackbarDuration.Short
            )
            shouldShowSnackBar = false
        }
    })