How to add a Bottom Sheet to an Android App

Android Bottom Sheet component slides up from the bottom showing more relevant content. You can notice bottom sheets in apps like map apps (bottom sheet reveals location, directions information), music players (Play bar sticks to bottom and opens when swipe up). The bottom sheet is the component of android design support library.

A view can be displayed as bottom sheet by attaching bottom sheet behavior to it. Bottom sheet behavior extends Behavior which has methods that coordinator layout calls to provide behavior to child views in response to motion events and positional changes of child views. In order to implement bottom sheet behavior in your app, the view you want to show as bottom sheet needs to be a direct child of coordinator layout.

There are two types of bottom sheets, Persistent bottom sheet and Modal bottom sheet.

  • Persistent Bottom Sheet. The Persistent bottom sheet displays in-app content. It will be displayed at the bottom of the screen making some portion of the content visible. When activated it opens the full content. The elevation of the persistent bottom sheet is same as app making it part of the app.

  • Modal Bottom Sheet. Modal bottom sheets have higher elevation than the app. These usually replaces menus or dialogs. Generally modal bottom sheets used to show deep-linked content from other apps.

Open build.gradle file and add support design.

dependencies {
    ...
    implementation 'com.android.support:design:27.1.0'
}

Programmatically show Bottom Sheet

Following is layout for MainActivity.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Content -->

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="5dp"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:layout_gravity="center"
            android:gravity="center">
            <TextView
                android:textSize="20dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Show BottomSheet"
                android:onClick="expandBs"/>
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>

    <!-- BottomSheet -->

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/bs"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:background="@color/colorPrimaryDark"
        app:layout_behavior="android.support.design.widget.
             BottomSheetBehavior">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:padding="5dp"
            android:gravity="center">
            <TextView
                android:id="@+id/tvInner"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@android:color/white"
                android:text="BottomSheet"
                android:textSize="25dp"/>
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>

There are few important attributes.

  • app:layout_behavior. This attribute makes the layout act as bottom sheet. The value should be android.support.design.widget.BottomSheetBehavior.
  • app:behavior_peekHeight. This is the height of the bottom sheet when it is minimized.
  • app:behavior_hideable. Makes bottom sheet hidden when swiped it down.

In a collapsed state, bottom sheet is not visible to users. If you want to let users know that there exists a bottom sheet on the screen, then some part of bottom sheet can be displayed by setting the peek height of bottom sheet using setPeekHeight method of bottom sheet behavior object.

You can handle bottom sheet events by implementing BottomSheetCallback callback methods. BottomSheetCallback has two call back methods. Method onStateChanged is called when bottom sheet’s state changes, for example, from collapsed state to expanded state. Another callback method onSlide is called when bottom sheet is dragged up or down.

Following is the MainActivity.

public class BottomSheetActivity extends AppCompatActivity {
    private BottomSheetBehavior bottomSheet = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_sheet);

        bottomSheet = BottomSheetBehavior.from( findViewById(R.id.bs));

        bottomSheet.setHideable(true);
        bottomSheet.setState(BottomSheetBehavior.STATE_HIDDEN);

        bottomSheet.setBottomSheetCallback(bottomSheetCallBack);
    }

    public void expandBs(View v){
        bottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED);
    }

    private BottomSheetBehavior.BottomSheetCallback bottomSheetCallBack = new BottomSheetBehavior
        .BottomSheetCallback(){
        public void onSlide (View bottomSheet, float slideOffset){
            TextView tvInner = bottomSheet.findViewById(R.id.tvInner);
            if(slideOffset < 0.4){
                tvInner.setText("Show more");
            }else{
                tvInner.setText("Full info");
            }
        }

        public void onStateChanged (View bottomSheet, int newState){
            TextView tvInner = bottomSheet.findViewById(R.id.tvInner);

            switch (newState) {
                case BottomSheetBehavior.STATE_HIDDEN:
                    tvInner.setText("STATE_HIDDEN");
                    break;
                case BottomSheetBehavior.STATE_EXPANDED:
                    tvInner.setText("STATE_EXPANDED");
                    break;
                case BottomSheetBehavior.STATE_COLLAPSED:
                    tvInner.setText("STATE_COLLAPSED");
                    break;
                case BottomSheetBehavior.STATE_DRAGGING:
                    tvInner.setText("STATE_DRAGGING");
                    break;
                case BottomSheetBehavior.STATE_SETTLING:
                    tvInner.setText("STATE_SETTLING");
                    break;
            }
        }
    };
}

Result

android_bottomsheet.png

Show Bottom Sheet on slide up

We can add app:behavior_peekHeight to layout and use slide up to show Bottom Sheet.

<!-- BottomSheet -->

<android.support.v4.widget.NestedScrollView
    android:id="@+id/bs"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:background="@color/colorPrimaryDark"
    app:behavior_peekHeight="50dp"
    app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="5dp"
        android:gravity="center">
        <TextView
            android:id="@+id/tvInner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:text="BottomSheet"
            android:textSize="25dp"/>
    </LinearLayout>
</android.support.v4.widget.NestedScrollView>

Modal Bottom Sheet or android bottom sheet dialog

Design support library provides BottomSheetDialogFragment and BottomSheetDialog classes to enable the creation of bottom sheet dialog. To create bottom sheet dialog, create a class which extends BottomSheetDialogFragment and override setupDialog method. In setupDialog method, inflate layout for bottom sheet dialog using view and set the dialog content.

Following is example of MainActivity.

public class MainActivity extends AppCompatActivity {
    private BottomSheetBehavior bottomSheet = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void expandBs(View v){
        ExampleOfBottomSheetDialogFragment frg =
                ExampleOfBottomSheetDialogFragment.newInstance("BottomSheetDialogFragment");
        frg.show(getSupportFragmentManager(), frg.getTag());
    }
}

Following is layout for MainActivity.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimary"
    android:orientation="vertical"
    android:padding="5dp"
    android:layout_gravity="center">
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white"
        android:textSize="25dp"
        android:layout_marginBottom="50dp"/>
</LinearLayout>

Following is example of BottomSheetDialogFragment.

public class ExampleOfBottomSheetDialogFragment extends BottomSheetDialogFragment {
    static String KEY_STRING = "KEY_STRING";

    static ExampleOfBottomSheetDialogFragment newInstance(String val) {
        ExampleOfBottomSheetDialogFragment frg = new ExampleOfBottomSheetDialogFragment();
        Bundle args = new Bundle();
        args.putString(KEY_STRING, val);
        frg.setArguments(args);
        return frg;
    }

    @SuppressLint("RestrictedApi")
    @Override
    public void setupDialog(Dialog dialog, int style) {
        super.setupDialog(dialog, style);

        String val = getArguments().getString(KEY_STRING);

        View v = View.inflate(getContext(), R.layout.bottom_sheet_dialog_fragment,  null);

        TextView tv = v.findViewById(R.id.tv);
        tv.setText(val);

        dialog.setContentView(v);
    }
}

Following is layout for BottomSheetDialogFragment.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimary"
    android:orientation="vertical"
    android:padding="5dp"
    android:layout_gravity="center">
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white"
        android:textSize="25dp"
        android:layout_marginBottom="50dp"/>
</LinearLayout>

Result

android_bottomsheet_dialog.png