Reputation: 675
What is the best way to implement bottom sheet for multiple screens in jetpack compose? Do we have to define Bottom sheet layout in each screen? Then what to do if we wanted our bottom sheet to overlap on bottom nav bar?
Upvotes: 3
Views: 2086
Reputation: 504
I have a different solution, that even though it isn't as elegant, works pretty well for the most part.
I have an app that uses bottom sheets extensively. Initially I used multiple ModalBottomSheetLayout
around the app, but soon I realized that was not going to work, due to some issued of having a ModalBottomSheetLayout
as a children of a Scaffold, instead of the other way around.
So what I do now, is have a single ModalBottomSheetLayout
as the topmost composable in the app, and pass to it a MutableState
called sheetContent
, that is stored in a global ViewModel
, that takes care of setting the bottom sheet content. I also store a lambda that is used to control the changes of the ModalBottomSheetState
:
typealias SheetContent = @Composable ColumnScope.() -> Unit
typealias OnBottomSheetStateChanges = (ModalBottomSheetValue) -> Boolean
class GlobalViewModel() {
// EmptyComposable is just a composable I created that contains an empty Text
// It is needed because if you don't pass anything
// to the ModalBottomSheetLayout it causes the app to crash
val sheetContent by mutableStateOf<SheetContent>({ EmptyComposable() })
var onSheetStateChanges by mutableStateOf<OnBottomSheetStateChanges>({ true })
(...)
}
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) {
viewModel.onSheetStateChanges(it).also {
// Resets the lambda
viewModel.onSheetStateChanges = { true }
}
}
ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
viewModel.sheetContent(this@ModalBottomSheetLayout)
}
) {
(...)
}
Whenever you need to open a bottom sheet, all you need to do is pass the bottom sheet content you want to GlobalViewModel.sheetContent
:
Button(
onClick = {
viewModel.sheetContent = {
MyBottomSheet()
}
coroutineScope.launch {
sheetState.show()
}
}
)
Also, if you want to observe the state of the bottom sheet, you can change the GlobalViewModel.onSheetStateChanges
when passing your sheet content:
viewModel.sheetContent = {
LaunchedEffect(true) {
viewModel.onSheetStateChanges = { value ->
if (value == ModalBottomSheetValue.Hidden) {
// do something with the information that the bottom sheet was closed
}
true
}
}
MyBottomSheet()
}
With this method, as long as you have access to the viewmodel and to the sheet state, you can easily change, open, close or observe the bottom sheet state from anywhere on your app.
Upvotes: 0
Reputation: 436
If you are using Jetpack Navigation Compose in your project, you might consider using Jetpack Navigation Compose Material to implement it.
For more detail, refer to the samples
Upvotes: 0
Reputation: 21
You can create a custom layout like
MyAppCustomLayout(
showBottomBar: Boolean = false,
state: ModalBottomSheetState = ModalBottomSheetState(initialValue =
ModalBottomSheetValue.Hidden),
sheetContent: @Composable () -> Unit = {},
content: @Composable () -> Unit)
{
ModalBottomSheetLayout(
sheetState = state,
sheetContent = { sheetContent() })
{
Scaffold(
bottomBar = if(showBottomBar)
{{
YourBottomNavigationView()
}}
else {{}})
{ content() }
}
}
And use it anywhere in your app like below.
val state = rememberModalBottomSheetState()
MyAppCustomLayout(
state = state,
sheetcontent = {
Column {
Text("Some bottomSheet content")
}
})
{
Column {
Text("Some content")
}
}
Upvotes: 1