Reputation: 19502
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
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
)
}
}
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