Seeing as I have multiple places where snackbars could be triggered, I want to have a central place in my app where I can handle showing/dismissing snackbars.
This is the structure of my app:
I've implemented a BaseViewModel that contains a StateFlow which should keep track of the SnackBar message (every other ViewModel inherits from this BaseViewModel):
@HiltViewModel
open class BaseViewModel @Inject constructor() : ViewModel() {
val _snackBarMessage = MutableStateFlow("")
val snackBarMessage: StateFlow<String> = _snackBarMessage
}
To test if the update of the StateFlow is triggered correctly, I've implemented a message that should update the StateFlow after every login:
private fun setSnackBarMessage() {
_snackBarMessage.value = "A wild snackBar appeared"
}
MainContent contains my Scaffold (incl. scaffoldState, snackbarHost), should react to changes in the snackBarMessage flow and display/dismiss the Snackbar when needed:
fun MainContent(...){
val message by viewModel.snackBarMessage.collectAsState()
LaunchedEffect(message) {
if (message.isNotEmpty() Timber.d("We got a snackbar")
}
Scaffold(...){...}
}
During debugging, I noticed that after every login the snackBarMessage value is updated correctly but MainContent does not get those updates which, in turn, means that the snackbar is never displayed.
Is there a reason why MainContent does not get those updates from the LoginComposable? Is it even possible to have a central instance of a snackbar or do I really need to handle snackbars separately in every Composable?
You can use this
@Composable
fun MainScreen() {
val coroutineScope = rememberCoroutineScope()
val showSnackBar: (
message: String?,
actionLabel: String,
actionPerformed: () -> Unit,
dismissed: () -> Unit
) -> Unit = { message, actionLabel, actionPerformed, dismissed ->
coroutineScope.launch {
val snackBarResult = scaffoldState.snackbarHostState.showSnackbar(
message = message.toString(),
actionLabel = actionLabel
)
when (snackBarResult) {
SnackbarResult.ActionPerformed -> actionPerformed.invoke()
SnackbarResult.Dismissed -> dismissed.invoke()
}
}
}
//Global using
showSnackBar.invoke(
"YOUR_MESSAGE",
"ACTION_LABEL",
{
//TODO ON ACTION PERFORMED
},
{
//TODO ON DISMISSED
}
)
}