Home > front end >  How to show the bottom sheet with transparent background in jetpack compose?
How to show the bottom sheet with transparent background in jetpack compose?

Time:11-17

My app consists of the home screen and on this screen, there is a button when users click on it they navigate to the login bottom sheet.

I am going to use this login bottom sheet elsewhere in the app so I prefer to make it a separate screen and navigate from home to login.

It is desirable to show the home screen as the background for the login screen. I mean the login bottom sheet's main content should be empty and transparent in order to see the home screen as the background. But instead of the home screen for background, the white background shows up.

Here are my codes:

LoginScreen:

@Composable
fun LoginScreen(
    loginViewModel: LoginViewModel = hiltViewModel()
) {
    val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
        bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed)
    )
    val coroutineScope = rememberCoroutineScope()

    BottomSheetScaffold(
        scaffoldState = bottomSheetScaffoldState,
        sheetContent = {
            LoginContent()
        },
        sheetPeekHeight = 400.dp,
        sheetShape = RoundedCornerShape(topEnd = 52.dp, topStart = 52.dp),
        backgroundColor = Color.Transparent
    ) {
        Box(modifier = Modifier.fillMaxSize().background(color = Color.Transparent)) {

        }
    }
}

HomeScreen:

@Composable
fun HomeScreen(
    modifier: Modifier = Modifier,
    viewModel: HomeViewModel = hiltViewModel(),
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(color = Color.White)
    ) {
        somecontent 
        ...
        ...
        ...
        Button(onClick = {
            viewModel.navigate(
                LoginDestination.route()
            )

        }) {
            Text("Go to the login screen")
        }
    }
}

I use navigation like this:

fun interface NavigationDestination {

    fun route(): String
    val arguments: List<NamedNavArgument>
        get() = emptyList()

    val deepLinks: List<NavDeepLink>
        get() = emptyList()
} 

and then Login destination overrides it:

object LoginDestination : NavigationDestination {
    override fun route(): String = "login"
}

and here is the implementation of the navigator:

@Singleton
internal class ClientNavigatorImpl @Inject constructor() : ClientNavigator {

    private val navigationEvents = Channel<NavigatorEvent>()
    override val destinations = navigationEvents.receiveAsFlow()

    override fun navigateUp(): Boolean =
        navigationEvents.trySend(NavigatorEvent.NavigateUp).isSuccess

    override fun popBackStack(): Boolean =
        navigationEvents.trySend(NavigatorEvent.PopBackStack).isSuccess

    override fun navigate(route: String, builder: NavOptionsBuilder.() -> Unit): Boolean =
        navigationEvents.trySend(NavigatorEvent.Directions(route, builder)).isSuccess
}

and the navigator event is:

sealed class NavigatorEvent {
    object NavigateUp : NavigatorEvent()
    object PopBackStack : NavigatorEvent()
    class Directions(
        val destination: String,
        val builder: NavOptionsBuilder.() -> Unit
    ) : NavigatorEvent()
}

CodePudding user response:

the way you are trying to show the LoginScreen won't work as you expected because when you navigate to LoginScreen it's like opening a new Screen, HomeScreen is then added to the backstack and not shown behind your LoginScreen. To make it work, try like this:

@Composable
fun HomeScreen(
    modifier: Modifier = Modifier,
    viewModel: HomeViewModel = hiltViewModel(),
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(color = Color.White)
    ) {
        Button(onClick = {
            //TODO: Add functionality
        }) {
            Text("Go to the login screen")
        }
    }
}

And change the LoginScreen parameters that you can give it a Composable:

@Composable
fun LoginScreen(
    loginViewModel: LoginViewModel = hiltViewModel(),
    screen: @Composable (() -> Unit)
) {
    val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
        bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed)
    )
    val coroutineScope = rememberCoroutineScope()

    BottomSheetScaffold(
        scaffoldState = bottomSheetScaffoldState,
        sheetContent = {
            //The Login Content needs to be here
*EDIT*

            BackHandler(enabled = true) {
              coroutineScope.launch {   
                 bottomSheetScaffoldState.bottomSheetState.collapse()
              }
            }
*EDIT*
        },
        sheetPeekHeight = 400.dp,
        sheetShape = RoundedCornerShape(topEnd = 52.dp, topStart = 52.dp),
        backgroundColor = Color.Transparent
    ) {
        screen() //Adds the content which is shown on the Screen behind bottomsheet
    }
}

And then use it somehow like this:

LoginScreen( /*YourLoginViewModel*/) {
    HomeScreen(Modifier, /*YourHomeScreenModel*/){
    }
}

Now your bottom sheet is shown all the time, to hide it you need to work with the BottomSheetState collapsed/expanded and the sheetPeekHeight = 400.dp, which you need to set to 0 that the sheet is hidden completely at first

In the end you need to implement that the BottomSheetState changes on the ButtonClick where you navigated to the Screen in your first attempt

Edit: Also don't use backgroundColor. To change the bottomSheets Background you need to use sheetBackgroundColor = Color.Transparent

CodePudding user response:

helpinghand You are right about handling the device back button. But now when the user presses the back button only the Login screen's sheet content collapse and the main content of the login screen which is the main content of the home screen remains. In order to it works I don't need the composable callback screen as a parameter for the login screen function and instead, I replace it with another callback like (callback: () -> Unit) and whenever want to get rid of login screen just invoke it in the login screen (for example when clicking outside the bottom sheet or collapsing it) and then in the home screen create a boolean mutable state for detecting when it needs to show the login screen and so in the trailing lambda make the state false.

  • Related