diff --git a/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt b/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt
index 56b5b3f052..5df163b640 100644
--- a/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt
+++ b/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt
@@ -31,9 +31,13 @@ class BottomSheetView(
private lateinit var dialogRootViewGroup: DialogRootViewGroup
private var eventDispatcher: EventDispatcher? = null
- private val screenHeight =
- context.resources.displayMetrics.heightPixels
- .toFloat()
+ private val rawScreenHeight = context.resources.displayMetrics.heightPixels.toFloat()
+ private val safeScreenHeight = (rawScreenHeight - getNavigationBarHeight()).toFloat()
+
+ private fun getNavigationBarHeight(): Int {
+ val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
+ return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0
+ }
private val onAttemptDismiss by EventDispatcher()
private val onSnapPointChange by EventDispatcher()
@@ -63,12 +67,12 @@ class BottomSheetView(
}
}
- var maxHeight = this.screenHeight
+ var maxHeight = this.safeScreenHeight
set(value) {
val px = dpToPx(value)
field =
- if (px > this.screenHeight) {
- this.screenHeight
+ if (px > this.safeScreenHeight) {
+ this.safeScreenHeight
} else {
px
}
@@ -153,6 +157,19 @@ class BottomSheetView(
// Presentation
+ private fun getHalfExpandedRatio(contentHeight: Float): Float {
+ return when {
+ // Full height sheets
+ contentHeight >= safeScreenHeight -> 0.99f
+ // Medium height sheets (>50% but <100%)
+ contentHeight >= safeScreenHeight / 2 ->
+ this.clampRatio(this.getTargetHeight() / safeScreenHeight)
+ // Small height sheets (<50%)
+ else ->
+ this.clampRatio(this.getTargetHeight() / rawScreenHeight)
+ }
+ }
+
private fun present() {
if (this.isOpen || this.isOpening || this.isClosing) return
@@ -172,12 +189,12 @@ class BottomSheetView(
val behavior = BottomSheetBehavior.from(it)
behavior.state = BottomSheetBehavior.STATE_HIDDEN
behavior.isFitToContents = true
- behavior.halfExpandedRatio = this.clampRatio(this.getTargetHeight() / this.screenHeight)
+ behavior.halfExpandedRatio = getHalfExpandedRatio(contentHeight)
behavior.skipCollapsed = true
behavior.isDraggable = true
behavior.isHideable = true
- if (contentHeight >= this.screenHeight || this.minHeight >= this.screenHeight) {
+ if (contentHeight >= this.safeScreenHeight || this.minHeight >= this.safeScreenHeight) {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
this.selectedSnapPoint = 2
} else {
@@ -227,11 +244,11 @@ class BottomSheetView(
bottomSheet?.let {
val behavior = BottomSheetBehavior.from(it)
- behavior.halfExpandedRatio = this.clampRatio(this.getTargetHeight() / this.screenHeight)
+ behavior.halfExpandedRatio = getHalfExpandedRatio(contentHeight)
- if (contentHeight > this.screenHeight && behavior.state != BottomSheetBehavior.STATE_EXPANDED) {
+ if (contentHeight > this.safeScreenHeight && behavior.state != BottomSheetBehavior.STATE_EXPANDED) {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
- } else if (contentHeight < this.screenHeight && behavior.state != BottomSheetBehavior.STATE_HALF_EXPANDED) {
+ } else if (contentHeight < this.safeScreenHeight && behavior.state != BottomSheetBehavior.STATE_HALF_EXPANDED) {
behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
}
}
diff --git a/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx b/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx
index acd46ce015..1fe592aa27 100644
--- a/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx
+++ b/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx
@@ -1,14 +1,17 @@
import * as React from 'react'
import {
Dimensions,
+ LayoutChangeEvent,
NativeSyntheticEvent,
Platform,
StyleProp,
View,
ViewStyle,
} from 'react-native'
+import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {requireNativeModule, requireNativeViewManager} from 'expo-modules-core'
+import {isIOS} from '#/platform/detection'
import {BottomSheetState, BottomSheetViewProps} from './BottomSheet.types'
import {BottomSheetPortalProvider} from './BottomSheetPortal'
import {Context as PortalContext} from './BottomSheetPortal'
@@ -81,12 +84,10 @@ export class BottomSheetNativeComponent extends React.Component<
return null
}
- const {children, backgroundColor, ...rest} = this.props
- const cornerRadius = rest.cornerRadius ?? 0
-
let extraStyles
if (isIOS15 && this.state.viewHeight) {
const {viewHeight} = this.state
+ const cornerRadius = this.props.cornerRadius ?? 0
if (viewHeight < screenHeight / 2) {
extraStyles = {
height: viewHeight,
@@ -99,39 +100,70 @@ export class BottomSheetNativeComponent extends React.Component<
return (
- {
+ const {height} = e.nativeEvent.layout
+ this.setState({viewHeight: height})
+ this.updateLayout()
}}
- containerBackgroundColor={backgroundColor}>
-
- {
- const {height} = e.nativeEvent.layout
- this.setState({viewHeight: height})
- this.updateLayout()
- }}>
- {children}
-
-
-
+ />
)
}
}
+
+function BottomSheetNativeComponentInner({
+ children,
+ backgroundColor,
+ onLayout,
+ onStateChange,
+ nativeViewRef,
+ extraStyles,
+ ...rest
+}: BottomSheetViewProps & {
+ extraStyles?: StyleProp
+ onStateChange: (
+ event: NativeSyntheticEvent<{state: BottomSheetState}>,
+ ) => void
+ nativeViewRef: React.RefObject
+ onLayout: (event: LayoutChangeEvent) => void
+}) {
+ const insets = useSafeAreaInsets()
+ const cornerRadius = rest.cornerRadius ?? 0
+
+ const sheetHeight = isIOS ? screenHeight - insets.top : screenHeight
+
+ return (
+
+
+
+ {children}
+
+
+
+ )
+}
diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx
index 597964e291..e70e4aef4c 100644
--- a/src/components/Dialog/index.tsx
+++ b/src/components/Dialog/index.tsx
@@ -26,7 +26,7 @@ import {isAndroid, isIOS} from '#/platform/detection'
import {useA11y} from '#/state/a11y'
import {useDialogStateControlContext} from '#/state/dialogs'
import {List, ListMethods, ListProps} from '#/view/com/util/List'
-import {atoms as a, useTheme} from '#/alf'
+import {atoms as a, tokens, useTheme} from '#/alf'
import {useThemeName} from '#/alf/util/useColorModeTheme'
import {Context, useDialogContext} from '#/components/Dialog/context'
import {
@@ -46,7 +46,7 @@ export {useDialogContext, useDialogControl} from '#/components/Dialog/context'
export * from '#/components/Dialog/shared'
export * from '#/components/Dialog/types'
export * from '#/components/Dialog/utils'
-// @ts-ignore
+
export const Input = createInput(TextInput)
export function Outer({
@@ -168,7 +168,9 @@ export function Outer({
onStateChange={onStateChange}
disableDrag={disableDrag}>
- {children}
+
+ {children}
+
)
@@ -196,7 +198,7 @@ export function Inner({children, style, header}: DialogInnerProps) {
export const ScrollableInner = React.forwardRef(
function ScrollableInner(
- {children, style, contentContainerStyle, header, ...props},
+ {children, contentContainerStyle, header, ...props},
ref,
) {
const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext()
@@ -216,13 +218,21 @@ export const ScrollableInner = React.forwardRef(
[],
)
- const basePading =
- (isIOS ? 30 : 50) + (isIOS ? keyboardHeight / 4 : keyboardHeight)
- const fullPaddingBase = insets.bottom + insets.top + basePading
- const fullPadding = isIOS ? fullPaddingBase : fullPaddingBase + 50
-
- const paddingBottom =
- nativeSnapPoint === BottomSheetSnapPoint.Full ? fullPadding : basePading
+ let paddingBottom = 0
+ if (isIOS) {
+ paddingBottom += keyboardHeight / 4
+ if (nativeSnapPoint === BottomSheetSnapPoint.Full) {
+ paddingBottom += insets.bottom + tokens.space.md
+ }
+ paddingBottom = Math.max(paddingBottom, tokens.space._2xl)
+ } else {
+ paddingBottom += keyboardHeight
+ if (nativeSnapPoint === BottomSheetSnapPoint.Full) {
+ paddingBottom += insets.top
+ }
+ paddingBottom +=
+ Math.max(insets.bottom, tokens.space._5xl) + tokens.space._2xl
+ }
const onScroll = (e: NativeSyntheticEvent) => {
if (!isAndroid) {
@@ -238,7 +248,6 @@ export const ScrollableInner = React.forwardRef(
return (
{/* Re-wrap with context since Dialogs are portal-ed to root */}
-
+
{children}
{isNative && showCancel && }