Comprehensive Guide to ModalBottomSheetLayout in Jetpack Compose

Prashant Kumar
7 min readMay 19, 2024

Jetpack Compose is Android’s modern toolkit for building native UI. It simplifies and accelerates UI development on Android with less code, powerful tools, and intuitive Kotlin APIs. One of the components provided by Jetpack Compose is the ModalBottomSheetLayout, which is used to implement modal bottom sheets. This guide will explore ModalBottomSheetLayout in detail, covering its usage, customization, integration with Scaffold, and best practices, providing a solid understanding for beginners.

What is ModalBottomSheetLayout?

ModalBottomSheetLayout is a Composable function in Jetpack Compose that provides a layout for displaying a modal bottom sheet. A modal bottom sheet is a type of bottom sheet that slides up from the bottom of the screen and forces the user to interact with it before they can return to the parent activity.

Key Features

  • Blocking Interaction: It blocks interaction with the rest of the UI until it is dismissed.
  • Content Display: It can display a variety of content such as lists, forms, or any other composables.
  • User Engagement: It provides an engaging way to show important content without navigating away from the current screen.

Basic Usage

To use ModalBottomSheetLayout, you need to include it in your composable function and provide the necessary parameters such as sheet content and state.

Example

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MyModalBottomSheet() {
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()
ModalBottomSheetLayout(
// Bottom sheet state
sheetState = sheetState,
sheetContent = {
// Content of the bottom sheet
Column(
modifier = Modifier.padding(16.dp)
) {
Text(text = "This is the content of the bottom sheet")
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { coroutineScope.launch { sheetState.hide() } }) {
Text("Close Sheet")
}
}
}
) {
// Main content
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { coroutineScope.launch { sheetState.show() } }) {
Text("Show Bottom Sheet")
}
}
}
}

In this example, clicking the "Show Bottom Sheet" button will display the bottom sheet, and clicking "Close Sheet" inside the bottom sheet will hide it.

Components

  1. Sheet State:
  • Managed by ModalBottomSheetState.
  • Can be Hidden, Expanded, or HalfExpanded.

2. Sheet Content:

  • Composables to be displayed in the bottom sheet.

3. Main Content:

  • The rest of the UI that remains visible when the bottom sheet is shown.

Managing Sheet State

ModalBottomSheetState is used to control the state of the bottom sheet. This state can be remembered and manipulated to show or hide the bottom sheet as needed.

  1. Creating the Sheet State: Use rememberModalBottomSheetState to create and remember the state of the bottom sheet. This function initializes the state with a specific value, such as ModalBottomSheetValue.Hidden.
  2. Controlling the Sheet State: Use show and hide methods of ModalBottomSheetState to display or hide the bottom sheet. These methods can be invoked within a coroutine scope to ensure proper state transitions.

Example

val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()

Button(onClick = { coroutineScope.launch { sheetState.show() } }) {
Text("Show Bottom Sheet")
}

Button(onClick = { coroutineScope.launch { sheetState.hide() } }) {
Text("Close Sheet")
}

Customization

Styling the Bottom Sheet

You can customize the appearance of the bottom sheet by modifying the sheetContent.

sheetContent = {
Column(
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
.padding(16.dp)
) {
// Custom content
}
}

Adding Gestures

ModalBottomSheetLayout supports gestures by default. You can further customize gesture behavior by handling gesture states.

Example

val sheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden,
confirmValueChange = { it != ModalBottomSheetValue.HalfExpanded }
)

Animation

You can add custom animations to the bottom sheet using the AnimatedVisibility composable.

AnimatedVisibility(
visible = sheetState.isVisible,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
// Animated content
}

Integrating with Scaffold

Scaffold is another powerful component in Jetpack Compose, providing a layout structure with predefined slots for common components like TopAppBar, BottomNavigation, and FloatingActionButton. Integrating ModalBottomSheetLayout with Scaffold requires careful consideration to ensure that elements like snackbars can still be displayed correctly.

Integration Approaches

There are two main approaches to integrating ModalBottomSheetLayout with Scaffold:

  1. Scaffold as the Parent
  2. Scaffold as the Child

1. Scaffold as the Parent

Placing Scaffold as the parent and ModalBottomSheetLayout as the child ensures that elements like the snackbar appear above the bottom sheet, making it suitable for use cases that require enhanced accessibility and user interaction.

Advantages:

  • Interactive Scaffold Components: The TopAppBar and FloatingActionButton remain interactive, allowing users to interact with them even when the bottom sheet is visible.
  • State Management: Easily control the bottom sheet’s state (hidden/visible) using the sheetState parameter of ModalBottomSheetLayout.
  • Snackbar Visibility: The Snackbar from the Scaffold will appear above the bottom sheet, making it visible to the user even when the bottom sheet is displayed.

Example:

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MyScaffoldWithBottomSheet() {
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()
val scaffoldState = rememberScaffoldState()

Scaffold(
scaffoldState = scaffoldState,
topBar = {
TopAppBar(
title = { Text("Scaffold with Bottom Sheet") }
)
},
floatingActionButton = {
FloatingActionButton(onClick = { coroutineScope.launch { sheetState.show() } }) {
Icon(Icons.Default.Add, contentDescription = "Show Bottom Sheet")
}
},
snackbarHost = { SnackbarHost(hostState = scaffoldState.snackbarHostState) }
) { padding ->
ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(text = "This is the bottom sheet content")
Button(onClick = { coroutineScope.launch { sheetState.hide() } }) {
Text("Close Sheet")
}
Button(onClick = {
coroutineScope.launch {
scaffoldState.snackbarHostState.showSnackbar("This is a Snackbar")
}
}) {
Text("Show Snackbar")
}
}
}
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Main content goes here")
}
}
}
}

In this example, the ModalBottomSheetLayout is within the Scaffold, allowing interaction with Scaffold components even when the bottom sheet is visible.

2. Scaffold as the Child

Placing ModalBottomSheetLayout as the parent and Scaffold as its child can simplify certain use cases where the overlay behavior of the snackbar is not required. In this approach, the bottom sheet will overlay the entire screen, including the TopAppBar and FloatingActionButton. The Snackbar from the Scaffold will not appear above the bottom sheet, making it less visible when the bottom sheet is displayed.

Advantages:

  • Simplicity: This approach is straightforward to implement for use cases where the overlay behavior of the Snackbar or other elements is not needed.
  • State Management: Easily control the bottom sheet’s state (hidden/visible) using the sheetState parameter of ModalBottomSheetLayout.

Example:

@Composable
fun MyBottomSheetWithScaffold() {
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()

ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(text = "This is the bottom sheet content")
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { coroutineScope.launch { sheetState.hide() } }) {
Text("Close Sheet")
}
}
}
) {
Scaffold(
topBar = {
TopAppBar(
title = { Text("Scaffold with Bottom Sheet") }
)
},
floatingActionButton = {
FloatingActionButton(onClick = { coroutineScope.launch { sheetState.show() } }) {
Icon(Icons.Default.Add, contentDescription = "Show Bottom Sheet")
}
}
) { padding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Main content goes here")
}
}
}
}

In this example, the ModalBottomSheetLayout wraps the Scaffold, which means the bottom sheet will cover the entire screen, including the TopAppBar and FloatingActionButton. Additionally, the Snackbar will not appear above the bottom sheet, making it less visible when the bottom sheet is displayed​.

Best Practices

  1. Avoid Overloading: Keep the content of the bottom sheet minimal to avoid overwhelming the user.
  2. Gestures: Utilize gestures for intuitive user interactions.
  3. Accessibility: Ensure that the bottom sheet is accessible by providing meaningful content descriptions and ensuring that it is navigable using screen readers.
  4. Performance: Optimize the performance by avoiding heavy computations inside the sheet content.
  5. State Management: Use proper state management to handle the visibility and interactions of the bottom sheet efficiently.

Conclusion

ModalBottomSheetLayout is a powerful component in Jetpack Compose that enhances the user experience by providing an engaging way to display important information without navigating away from the current screen. By following the best practices and customizing it to suit your needs, you can create a seamless and interactive user interface.

This guide covered the basics of ModalBottomSheetLayout, including its usage, customization, integration with Scaffold, and best practices. With this knowledge, you should be able to implement modal bottom sheets effectively in your Jetpack Compose applications.

Visual Examples

To make this guide more interactive, here are some visual examples:

Basic ModalBottomSheetLayout

Main UI
After clicking “Show Bottom Sheet”

ModalBottomSheetLayout with Scaffold

Scaffold Main Content
After clicking FloatingActionButton(Scaffold as a parent of ModalBottomSheetLayout)
After clicking “Show Snackbar” Button(Scaffold as a parent of ModalBottomSheetLayout)
After clicking FloatingActionButton(Scaffold as a child of ModalBottomSheetLayout)

These examples illustrate how the modal bottom sheet overlays the main UI, providing an effective way to display additional information or actions without disrupting the user’s flow.

--

--