Reza Faraji
Reza Faraji

Reputation: 675

Jetpack compose Bottom sheet in multi screens

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

Answers (3)

jpegoraro
jpegoraro

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

laomo
laomo

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

Mbah Javis
Mbah Javis

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

Related Questions