Chandra Sekhar
Chandra Sekhar

Reputation: 19502

Snackbar not showing in Jetpack Compose

My screen has one button and on click of it, I want to show a snackbar for short duration. So here is my composable:

@Composable
fun RegisterLocation(
    navController: NavController,
    locationVm: LocationViewModel = LocationViewModel()
) {
    val uiState = locationVm.locationState.collectAsState(LocationUiState.Init)
    val snackbarHostState = remember { SnackbarHostState() }
    when (uiState.value) {
        is LocationUiState.Init -> RegisterLocation(locationVm, snackbarHostState)
        is LocationUiState.Invalid -> {
            LaunchedEffect(locationVm.locationState.value) {
                snackbarHostState.showSnackbar(
                    "Failed Creating Location. Try Again!!!"
                )
            }
        }
        is LocationUiState.Valid -> navController.popBackStack()
    }
}

@Composable
fun RegisterLocation(locationVm: WLocationViewModel, snackbarHostState: SnackbarHostState) {
    Box(Modifier.fillMaxSize()) {
        Column {
            SubmitLocation(locationVm)
        }

        Column(
            Modifier
                .fillMaxWidth()
                .align(Alignment.BottomCenter)
        ) {
            Text(text = "Hello") // <-- This is a dummy text and it shows in the UI. Moreover, it animates up to give space for snackbar. But the snackbar doesn't show up :(
            RegistrationError(snackbarHostState)
        }
    }
}

@ExperimentalCoroutinesApi
@Composable
fun SubmitLocation(vm: WLocationViewModel) {
    Button(
        onClick = { vm.onCreate(LocationUiState.Invalid) },
        modifier = Modifier.fillMaxWidth()
    ) {
        Text(text = "Submit Location")
    }
}

@Composable
fun RegistrationError(hostState: SnackbarHostState) {
    SnackbarHost(
        hostState = hostState,
        modifier = Modifier.fillMaxWidth(),
        snackbar = {
            Snackbar {
                Text(text = "Hello", color = Color.White)
            }
        }
    )
}

What is wrong in this code? Any help would be appreciated.

Upvotes: 9

Views: 10430

Answers (1)

Santanu Sur
Santanu Sur

Reputation: 11477

SnackbarHostState shows a snackbar in a Scaffold or can be passed as a param to SnackBar composable.

Why the snackbar host does not show the snackbar ?

Whenever the state is changed the snackBar does NOT have any Scaffold or a SnackbarHost to show the content. ( snackbarHostState can also be passed while composing a SnackBar which u have already done - Check solution 2 )

You are neither using a Scaffold nor composing a SnackBarHost by using the snackBarHostState during the Invalid state.

Solution 1

We need to attach the SnackbarHostState at a Scaffold to make it show up.

when (uiState.value) {
   is LocationUiState.Init -> RegisterLocation(locationVm, snackbarHostState)
   is LocationUiState.Invalid -> {
         LaunchedEffect(locationVm.locationState.value) {
            snackbarHostState.showSnackbar(
                    "Failed Creating Location. Try Again!!!",
                    "label",
                    duration = SnackbarDuration.Long
            )
         }
         Scaffold(
                // attach snackbar host state to the scaffold
             scaffoldState = rememberScaffoldState(snackbarHostState = snackbarHostState),
             content = { } // content is not mandatory
         )
   }
}

1

Solution 2:-

You are never composing RegisterLocation(locationVm, snackbarHostState) in the Invalid state. As per the code, if we compose the RegisterLocation composable in the Invalid state, then also a snackbar added as a composable should show up.

when (uiState.value) {
        is LocationUiState.Init -> RegisterLocation(locationVm, snackbarHostState)
        is LocationUiState.Invalid -> {
            RegistrationError(hostState = snackbarHostState)
            LaunchedEffect(locationVm.locationState.value) {
                snackbarHostState.showSnackbar(
                    "Failed Creating Location. Try Again!!!",
                    "label",
                    duration = SnackbarDuration.Long
                )
            }
        }
}

Update ( to elaborate )

Compose maintains a GroupArray under the hood , which contains all the UI elements drawn in the screen based on their position. So during a re-compose it compares and shows only the UI elements that are drawn in during the re-composition and removes the other components which were built during the previous composition that are redundant ( as in the UI components we dont visit during re-composition are removed ).

For your case:- The states are totally different and we are never visiting the Init state during the second composition. So, compose sees during re-composition we are never visiting the Init state so it removes the Init UI from its memory. ( using positional memoisation under the hood ).

Please check compose under the hood

Upvotes: 14

Related Questions