Skip to main content

MapBottomSheet

The MapBottomSheet is a custom bottom sheet built with AnchoredDraggable (not Material's BottomSheetScaffold) for full control over anchor positions and nested scroll behavior. For the rationale behind this choice, see the design decisions.


SheetAnchor State Machine​

enum class SheetAnchor {
Collapsed,
HalfExpanded,
Expanded,
}

Anchor positions are computed from the available height:

AnchorOffset (from top)Description
CollapsedavailableHeight - dragHandleHeightOnly the drag handle visible (24dp)
HalfExpandedavailableHeight * (1 - 0.50)50% of screen (or content-fitted for detail views)
ExpandedavailableHeight * (1 - 0.90)90% of screen

For fixed-height content (device info, notification info), HalfExpanded snaps to the actual content height instead of 50%:

val halfOffset = if (isFixedContent && measuredContentHeight > 0) {
val contentSheetHeight = measuredContentHeight + collapsedHeightPx
(availableHeightPx - contentSheetHeight).coerceAtLeast(expandedOffset)
} else {
availableHeightPx * (1f - Dimensions.BottomSheet.HALF_FRACTION)
}

BottomSheet Dimension Tokens​

TokenValue
dragHandleHeight24.dp
dragHandleWidth28.dp
dragHandleThickness3.dp
topCornerRadius16.dp
headerHeight40.dp
EXPANDED_FRACTION0.90f
HALF_FRACTION0.50f

Nested Scroll Integration​

The BottomSheetNestedScrollConnection coordinates between the sheet's drag and the inner LazyColumn scroll:

// Swiping up: only move sheet if Collapsed
dy < 0 -> sheetState.currentValue == SheetAnchor.Collapsed
// Swiping down: collapse sheet when list is at top
dy > 0 -> !lazyListState.canScrollBackward

Fling behavior:

  • Fling up from Collapsed β†’ animate to HalfExpanded
  • Fling up at HalfExpanded (list can't scroll further) β†’ animate to Expanded
  • Fling down (list at top) β†’ step down one anchor (Expanded β†’ HalfExpanded β†’ Collapsed)

SheetContentMode​

The sheet supports three content modes via a sealed class:

sealed class SheetContentMode {
data object DeviceList : SheetContentMode()
data class DeviceInfo(val device: DeviceWithPosition) : SheetContentMode()
data class NotificationInfo(val detail: NotificationDetail, val deviceName: String) : SheetContentMode()
}

Content transitions use AnimatedContent with fadeIn togetherWith fadeOut. When switching to DeviceInfo or NotificationInfo, the sheet auto-animates to HalfExpanded.