How to set dynamic position of snackbar in jetpack compose?

2.9k views Asked by At

I have a Scaffold with a NavHost as its content. NavHost host 2 screens. One screen contains the bottomNavigationBar and another screen doesnot.

How can I display create a snack bar that can be used globally which is displayed at the bottom of the screen if no bottomNavigationView is there but adds a bottom padding of height = bottomNavigationHeight if the screen contains bottomNavigationView?

Below is my main content:


@Composable
fun MainScreen() {
    val navController = rememberNavController()
    val scaffoldState = rememberScaffoldState()
    Scaffold(
        scaffoldState = scaffoldState
    ) {
        NavHost(navController = navController, "first") {
            composable("first") {
                FirstScreen()
            }
            composable("second") {
                SecondScreen()
            }
        }

    }
}

My first screen.

// No bottom navigation
@Composable
fun FirstScreen() {
    Text("Hello World")
}

My second screen:

// Contains bottom nav
@Composable
fun SecondScreen() {
    Scaffold(bottomBar = { MyBottomBar() }) {

    }
}

Now, I can use CompositionLocal to send snackBarHostState from scaffold state to all my screens. But, I want first screen to display the snack bar without bottom padding and second screen to display the snack bar with padding for the bottomNavView.

How can I achieve the dynamic position of snackBar??

P.S. This is a simplified version of my issue. I have about 6-7 screen for my MainContent and many screens for the screen with bottom nav.

1

There are 1 answers

0
Quốc Hùng On

I think you can follow this article: https://www.devbitsandbytes.com/configuring-snackbar-jetpack-compose-using-scaffold-with-bottom-navigation/

First you can create a DefaultSnackbar composable:

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun DefaultSnackbar(
    snackbarHostState: SnackbarHostState,
    modifier: Modifier = Modifier,
    onDismiss: () -> Unit = { }
) {
    SnackbarHost(
        hostState = snackbarHostState,
        snackbar = { data ->
            Snackbar(
                modifier = Modifier.padding(16.dp),
                content = {
                    Text(
                        text = data.message,
                        style = MaterialTheme.typography.body2
                    )
                },
                action = {
                    data.actionLabel?.let { actionLabel ->
                        TextButton(onClick = onDismiss) {
                            Text(
                                text = actionLabel,
                                color= MaterialTheme.colors.primary,
                                style = MaterialTheme.typography.body2
                            )
                        }
                    }
                }
            )
        },
        modifier = modifier
            .fillMaxWidth()
            .wrapContentHeight(Alignment.Bottom)
    )
}

Then handle the SnackbarHostState in both FirstScreen and SecondScreen like that:

    val scaffoldState = rememberScaffoldState()

    Scaffold(
        scaffoldState = scaffoldState,
        snackbarHost = { scaffoldState.snackbarHostState }
    ) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(scaffoldPadding)
        ) {
            Box(modifier = Modifier.weight(1f)) {
                // Content of the screen
                DefaultSnackbar(
                    snackbarHostState = scaffoldState.snackbarHostState,
                    modifier = Modifier.align(Alignment.BottomCenter)
                ) {
                    scaffoldState.snackbarHostState.currentSnackbarData?.dismiss()
                }
            }
            // The bottom bar if have
        }
    }