cascade builds nested popup menus with smooth height animations. It is designed to be a drop-in replacement for PopupMenu so using it in your project is beautifully only a word away. Try out the sample app to see it in action.


implementation "me.saket.cascade:cascade:1.3.0"
- val popup = PopupMenu(context, anchor)
+ val popup = CascadePopupMenu(context, anchor)

Use as Toolbar's overflow menu

toolbar.overrideAllPopupMenus { context, anchor ->
  CascadePopupMenu(context, anchor)

// The lambda can be collapsed into a reference
// if you're only using the two-param constructor.
toolbar.overrideAllPopupMenus(with = ::CascadePopupMenu)


cascade is great for apps that prefer applying dynamic themes at runtime, which PopupMenu makes it extremely hard to do so. By providing a CascadePopupMenu.Styler object, you can adjust colors, spacings and text styles from Kotlin (example).

CascadePopupMenu(context, anchor, styler = CascadePopupMenu.Styler(...))

By default, cascade will pick up values from your theme in the same way as PopupMenu would.

<style name="AppTheme">
  <item name="popupMenuStyle">@style/PopupMenuStyle</item>
  <item name="colorControlNormal">@color/menu_icon_color</item>
  <item name="android:textColorPrimary">@color/menu_item_text_color</item>
  <item name="android:textColorSecondary">@color/menu_title_text_color</item>

<style name="PopupMenuStyle" parent="@style/Widget.AppCompat.PopupMenu">
  <item name="android:popupBackground">...</item>
  <item name="android:popupElevation">...</item>

For sub-menus, cascade will automatically navigate to the parent menu when the title is clicked. For manual navigation, CascadePopupMenu#navigateBack() or CascadeBackNavigator can be used."Remove").also {
  it.setHeaderTitle("Are you sure?")
  it.add("Burn them all")
  it.add("Take me back").setOnMenuItemClickListener {

Custom layouts

cascade was originally inspired by Google Drive's menu that uses a variety of complex controls. For apps that want to create something similar, a batteries-included CascadePopupWindow is provided for use with custom layouts.

val popup = CascadePopupWindow(context)
popup.contentView.addView(CustomMenuView(context))  // Also see contentView.goBack().