Bottom sheets are surfaces containing supplementary content that are anchored to the bottom of the screen.
Contents
Before you can use Material bottom sheets, you need to add a dependency to the Material Components for Android library. For more information, go to the Getting started page.
Standard bottom sheet basic usage:
<androidx.coordinatorlayout.widget.CoordinatorLayout
...>
<FrameLayout
...
android:id="@+id/standard_bottom_sheet"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<!-- Bottom sheet contents. -->
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Modal bottom sheet basic usage:
class ModalBottomSheet : BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.modal_bottom_sheet_content, container, false)
companion object {
const val TAG = "ModalBottomSheet"
}
}
class MainActivity : AppCompatActivity() {
...
val modalBottomSheet = ModalBottomSheet()
modalBottomSheet.show(supportFragmentManager, ModalBottomSheet.TAG)
...
}
More information on each individual section, below.
There are several attributes that can be used to adjust the behavior of both standard and modal bottom sheets.
Behavior attributes can be applied to standard bottom sheets in xml by setting
them on a child View
set to app:layout_behavior
, or programmatically:
val standardBottomSheetBehavior = BottomSheetBehavior.from(standardBottomSheet)
// Use this to programmatically apply behavior attributes
Behavior attributes can be applied to modal bottom sheets using app-level theme attributes and styles:
<style name="ModalBottomSheet" parent="Widget.Material3.BottomSheet.Modal">
<!-- Apply attributes here -->
</style>
<style name="ModalBottomSheetDialog" parent="ThemeOverlay.Material3.BottomSheetDialog">
<item name="bottomSheetStyle">@style/ModalBottomSheet</item>
</style>
<style name="AppTheme" parent="Theme.Material3.*">
<item name="bottomSheetDialogTheme">@style/ModalBottomSheetDialog</item>
</style>
Or programmatically:
val modalBottomSheetBehavior = (modalBottomSheet.dialog as BottomSheetDialog).behavior
// Use this to programmatically apply behavior attributes
More information about these attributes and their default values is available in the behavior attributes section.
In order to save and restore specific behaviors of the bottom sheet on configuration change, the following flags can be set (or combined with bitwise OR operations):
SAVE_PEEK_HEIGHT
:app:behavior_peekHeight
is preserved.SAVE_HIDEABLE
:app:behavior_hideable
is preserved.SAVE_SKIP_COLLAPSED
:app:behavior_skipCollapsed
is preserved.SAVE_FIT_TO_CONTENTS
:app:behavior_fitToContents
is preserved.SAVE_ALL
: All aforementioned attributes are preserved.SAVE_NONE
: No attribute is preserved. This is the default value.
Behaviors can also be set in code:
bottomSheetBehavior.saveFlags = BottomSheetBehavior.SAVE_ALL
Or in xml using the app:behavior_saveFlags
attribute.
Standard and modal bottom sheets have the following states:
STATE_COLLAPSED
: The bottom sheet is visible but only showing its peek height. This state is usually the 'resting position' of a bottom sheet, and should have enough height to indicate there is extra content for the user to interact with.STATE_EXPANDED
: The bottom sheet is visible at its maximum height and it is neither dragging nor settling (see below).STATE_HALF_EXPANDED
: The bottom sheet is half-expanded (only applicable ifbehavior_fitToContents
has been set to false), and is neither dragging nor settling (see below).STATE_HIDDEN
: The bottom sheet is no longer visible and can only be re-shown programmatically.STATE_DRAGGING
: The user is actively dragging the bottom sheet up or down.STATE_SETTLING
: The bottom sheet is settling to a specific height after a drag/swipe gesture. This will be the peek height, expanded height, or 0, in case the user action caused the bottom sheet to hide.
You can set a state on the bottom sheet:
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
Note: STATE_SETTLING
and STATE_DRAGGING
should not be set programmatically.
A BottomSheetCallback
can be added to a BottomSheetBehavior
:
val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
// Do something for new state.
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
// Do something for slide offset.
}
}
// To add the callback:
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallback)
// To remove the callback:
bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallback)
BottomSheetBehavior
can automatically handle insets (such as for
edge to edge) by
specifying any of these to true on the view:
app:paddingBottomSystemWindowInsets
app:paddingLeftSystemWindowInsets
app:paddingRightSystemWindowInsets
app:paddingTopSystemWindowInsets
On API 21 and above the modal bottom sheet will be rendered fullscreen (edge to
edge) if the navigation bar is transparent and app:enableEdgeToEdge
is true.
Insets can be added automatically if any of the padding attributes above are set
to true in the style, either by updating the style passed to the constructor, or
by updating the default style specified by the ?attr/bottomSheetDialogTheme
attribute in your theme.
BottomSheetDialog
will also add padding to the top when the bottom sheet
slides under the status bar, to prevent content from being drawn underneath it.
The contents within a bottom sheet should follow their own accessibility guidelines, such as setting content descriptions for images.
Standard bottom sheets co-exist with the screen’s main UI region and allow for simultaneously viewing and interacting with both regions. They are commonly used to keep a feature or secondary content visible on screen when content in the main UI region is frequently scrolled or panned.
BottomSheetBehavior
is applied to a child of
CoordinatorLayout
to make that child a persistent bottom sheet, which is a view that comes up
from the bottom of the screen, elevated over the main content. It can be dragged
vertically to expose more or less content.
API and source code:
BottomSheetBehavior
The following example shows a standard bottom sheet in its collapsed and expanded states:
BottomSheetBehavior
works in tandem with CoordinatorLayout
to let you
display content on a bottom sheet, perform enter/exit animations, respond to
dragging/swiping gestures, etc.
Apply the BottomSheetBehavior
to a direct child View
of CoordinatorLayout
:
<androidx.coordinatorlayout.widget.CoordinatorLayout
...>
<FrameLayout
android:id="@+id/standard_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="?attr/bottomSheetStyle"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<!-- Bottom sheet contents. -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title"
.../>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/supporting_text"
.../>
<Button
android:id="@+id/bottomsheet_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/action"
.../>
<com.google.android.material.switchmaterial.SwitchMaterial
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/switch_label"/>
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
In this example, the bottom sheet is the FrameLayout
.
Modal bottom sheets present a set of choices while blocking interaction with the rest of the screen. They are an alternative to inline menus and simple dialogs on mobile devices, providing additional room for content, iconography, and actions.
BottomSheetDialogFragment
is a thin layer on top of the regular support
library Fragment that renders your fragment as a modal bottom sheet,
fundamentally acting as a dialog.
Modal bottom sheets render a shadow on the content below them, to indicate that they are modal. If the content outside of the dialog is tapped, the bottom sheet is dismissed. Modal bottom sheets can be dragged vertically and dismissed by sliding them down completely.
API and source code:
BottomSheetDialogFragment
The following example shows a modal bottom sheet in its collapsed and expanded states:
First, subclass BottomSheetDialogFragment
and overwrite onCreateView
to
provide a layout for the contents of the sheet (in this example, it's
modal_bottom_sheet_content.xml
):
class ModalBottomSheet : BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.modal_bottom_sheet_content, container, false)
companion object {
const val TAG = "ModalBottomSheet"
}
}
Then, inside an AppCompatActivity
, to show the bottom sheet:
val modalBottomSheet = ModalBottomSheet()
modalBottomSheet.show(supportFragmentManager, ModalBottomSheet.TAG)
BottomSheetDialogFragment
is a subclass of AppCompatFragment
, which means
you need to use Activity.getSupportFragmentManager()
.
Note: Don't call setOnCancelListener
or setOnDismissListener
on a
BottomSheetDialogFragment
. You can override
onCancel(DialogInterface)
or onDismiss(DialogInterface)
if necessary.
Bottom sheets have a sheet, content, and, if modal, a scrim.
- Sheet
- Content
- Scrim (in modal bottom sheets)
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Color | app:backgroundTint |
N/A | ?attr/colorSurface |
Shape | app:shapeAppearance |
N/A | ?attr/shapeAppearanceLargeComponent |
Elevation | android:elevation |
N/A | 3dp |
Max width | android:maxWidth |
setMaxWidth getMaxWidth |
640dp |
Max height | android:maxHeight |
setMaxHeight getMaxHeight |
N/A |
More info about these attributes and how to use them in the setting behavior section.
Behavior | Related method(s) | Default value |
---|---|---|
app:behavior_peekHeight |
setPeekHeight getPeekHeight |
auto |
app:behavior_hideable |
setHideable isHideable |
false for standardtrue for modal |
app:behavior_skipCollapsed |
setSkipCollapsed getSkipCollapsed |
false |
app:behavior_fitToContents |
setFitToContents isFitToContents |
true |
app:behavior_draggable |
setDraggable isDraggable |
true |
app:behavior_halfExpandedRatio |
setHalfExpandedRatio getHalfExpandedRatio |
0.5 |
app:behavior_expandedOffset |
setExpandedOffset getExpandedOffset |
0dp |
To save behavior on configuration change:
Attribute | Related method(s) | Default value |
---|---|---|
app:behavior_saveFlags |
setSaveFlags getSaveFlags |
SAVE_NONE |
Element | Default value |
---|---|
Default style (modal) | @style/Widget.Material3.BottomSheet.Modal |
Default style theme attribute:?attr/bottomSheetStyle
Element | Theme overlay |
---|---|
Default theme overlay | ThemeOverlay.Material3.BottomSheetDialog |
Default theme overlay attribute: ?attr/bottomSheetDialogTheme
See the full list of styles, attrs, and themes and theme overlays.
Bottom sheets support Material Theming, which can customize color and shape.
API and source code:
BottomSheetBehavior
BottomSheetDialogFragment
The following example shows a bottom sheet with Material Theming, in its collapsed and expanded states.
Setting the theme attribute bottomSheetDialogTheme
to your custom
ThemeOverlay
will affect all bottom sheets.
In res/values/themes.xml
:
<style name="Theme.App" parent="Theme.Material3.*">
...
<item name="bottomSheetDialogTheme">@style/ThemeOverlay.App.BottomSheetDialog</item>
</style>
<style name="ThemeOverlay.App.BottomSheetDialog" parent="ThemeOverlay.Material3.BottomSheetDialog">
<item name="bottomSheetStyle">@style/ModalBottomSheetDialog</item>
</style>
In res/values/styles.xml
:
<style name="ModalBottomSheetDialog" parent="Widget.Material3.BottomSheet.Modal">
<item name="backgroundTint">@color/shrine_pink_light</item>
<item name="shapeAppearance">@style/ShapeAppearance.App.LargeComponent</item>
</style>
<style name="ShapeAppearance.App.LargeComponent" parent="ShapeAppearance.Material3.LargeComponent">
<item name="cornerFamily">cut</item>
<item name="cornerSize">24dp</item>
</style>
Note: The benefit of using a custom ThemeOverlay
is that any changes to
your main theme, such as updated colors, will be reflected in the bottom sheet,
as long as they're not overridden in your custom theme overlay. If you use a
custom Theme
instead, by extending from one of the
Theme.Material3.*.BottomSheetDialog
variants, you will have more control over
exactly what attributes are included in each, but it also means you'll have to
duplicate any changes that you've made in your main theme into your custom
theme.