Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions android/src/main/java/com/swmansion/rnscreens/Screen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
import com.swmansion.rnscreens.bottomsheet.BottomSheetMetrics
import com.swmansion.rnscreens.bottomsheet.isSheetFitToContents
import com.swmansion.rnscreens.bottomsheet.useSingleDetent
import com.swmansion.rnscreens.bottomsheet.usesFormSheetPresentation
Expand Down Expand Up @@ -142,6 +143,9 @@ class Screen(
if (usesFormSheetPresentation()) {
if (isSheetFitToContents()) {
sheetBehavior?.useSingleDetent(height)
// During the initial call in `onCreateView`, insets are not yet available,
// so we need to request an additional layout pass later to account for them.
requestLayout()
}

if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
Expand Down Expand Up @@ -530,6 +534,11 @@ class Screen(
}
}

fun isOverflowingStatusBar(
topInset: Int,
metrics: BottomSheetMetrics,
): Boolean = metrics.maxSheetHeight >= metrics.availableHeight - topInset

enum class StackPresentation {
PUSH,
MODAL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,37 @@ import android.view.View
import com.google.android.material.bottomsheet.BottomSheetBehavior

internal fun <T : View> BottomSheetBehavior<T>.useSingleDetent(
height: Int? = null,
maxAllowedHeight: Int? = null,
forceExpandedState: Boolean = true,
): BottomSheetBehavior<T> {
this.skipCollapsed = true
this.isFitToContents = true
if (forceExpandedState) {
this.state = BottomSheetBehavior.STATE_EXPANDED
}
height?.let {
maxHeight = height
maxAllowedHeight?.let {
maxHeight = maxAllowedHeight
}
return this
}

internal fun <T : View> BottomSheetBehavior<T>.useTwoDetents(
@BottomSheetBehavior.StableState state: Int? = null,
firstHeight: Int? = null,
secondHeight: Int? = null,
maxAllowedHeight: Int? = null,
): BottomSheetBehavior<T> {
skipCollapsed = false
isFitToContents = true
state?.let { this.state = state }
firstHeight?.let { peekHeight = firstHeight }
secondHeight?.let { maxHeight = secondHeight }
maxAllowedHeight?.let { maxHeight = maxAllowedHeight }
return this
}

internal fun <T : View> BottomSheetBehavior<T>.useThreeDetents(
@BottomSheetBehavior.StableState state: Int? = null,
firstHeight: Int? = null,
maxAllowedHeight: Int? = null,
halfExpandedRatio: Float? = null,
expandedOffsetFromTop: Int? = null,
): BottomSheetBehavior<T> {
Expand All @@ -43,5 +44,6 @@ internal fun <T : View> BottomSheetBehavior<T>.useThreeDetents(
firstHeight?.let { this.peekHeight = firstHeight }
halfExpandedRatio?.let { this.halfExpandedRatio = halfExpandedRatio }
expandedOffsetFromTop?.let { this.expandedOffset = expandedOffsetFromTop }
maxAllowedHeight?.let { maxHeight = maxAllowedHeight }
return this
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.swmansion.rnscreens.bottomsheet

import com.swmansion.rnscreens.Screen

data class BottomSheetMetrics(
val availableHeight: Int,
val maxDetent: Double,
val maxSheetHeight: Int,
)

fun getSheetMetrics(
screen: Screen,
availableHeight: Int,
sheetHeight: Int,
): BottomSheetMetrics {
val maxDetent = screen.sheetDetents.lastOrNull() ?: 1.0

val maxSheetHeight =
when {
screen.usesFormSheetPresentation() && screen.isSheetFitToContents() -> sheetHeight
else -> (availableHeight * maxDetent).toInt()
}

return BottomSheetMetrics(
availableHeight = availableHeight,
maxDetent = maxDetent,
maxSheetHeight = maxSheetHeight,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class SheetDelegate(
} else {
(screen.sheetDetents.first() * containerHeight).toInt()
}
useSingleDetent(height = height)
useSingleDetent(maxAllowedHeight = height)
}

2 ->
Expand All @@ -153,7 +153,7 @@ class SheetDelegate(
screen.sheetDetents.count(),
),
firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(),
secondHeight = (screen.sheetDetents[1] * containerHeight).toInt(),
maxAllowedHeight = (screen.sheetDetents.last() * containerHeight).toInt(),
)

3 ->
Expand All @@ -164,6 +164,7 @@ class SheetDelegate(
screen.sheetDetents.count(),
),
firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(),
maxAllowedHeight = (screen.sheetDetents.last() * containerHeight).toInt(),
halfExpandedRatio = (screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat(),
expandedOffsetFromTop = ((1 - screen.sheetDetents[2]) * containerHeight).toInt(),
)
Expand Down Expand Up @@ -212,19 +213,20 @@ class SheetDelegate(
when (screen.sheetDetents.count()) {
1 ->
behavior.useSingleDetent(
height = (screen.sheetDetents.first() * containerHeight).toInt(),
maxAllowedHeight = (screen.sheetDetents.last() * containerHeight).toInt(),
forceExpandedState = false,
)

2 ->
behavior.useTwoDetents(
firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(),
secondHeight = (screen.sheetDetents[1] * containerHeight).toInt(),
maxAllowedHeight = (screen.sheetDetents.last() * containerHeight).toInt(),
)

3 ->
behavior.useThreeDetents(
firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(),
maxAllowedHeight = (screen.sheetDetents.last() * containerHeight).toInt(),
halfExpandedRatio = (screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat(),
expandedOffsetFromTop = ((1 - screen.sheetDetents[2]) * containerHeight).toInt(),
)
Expand All @@ -244,46 +246,51 @@ class SheetDelegate(
): WindowInsetsCompat {
val isImeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val imeInset = insets.getInsets(WindowInsetsCompat.Type.ime())
val prevInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())

if (isImeVisible) {
isKeyboardVisible = true
keyboardState = KeyboardVisible(imeInset.bottom)
sheetBehavior?.let {
this.configureBottomSheetBehaviour(it, keyboardState)
}

val prevInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
return WindowInsetsCompat
.Builder(insets)
.setInsets(
WindowInsetsCompat.Type.navigationBars(),
Insets.of(
prevInsets.left,
prevInsets.top,
prevInsets.right,
0,
),
).build()
} else {
sheetBehavior?.let {
if (isKeyboardVisible) {
this.configureBottomSheetBehaviour(it, KeyboardDidHide)
} else if (keyboardState != KeyboardNotVisible) {
this.configureBottomSheetBehaviour(it, KeyboardNotVisible)
} else {
}
}

keyboardState = KeyboardNotVisible
isKeyboardVisible = false
}

val prevInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
// We're taking the metrics of the rootView, not screen to support nested navigators
val availableHeight = screen.rootView.height

val metrics =
getSheetMetrics(
screen = screen,
availableHeight = availableHeight,
sheetHeight = screen.height,
)

val newTopInset =
if (screen.isOverflowingStatusBar(prevInsets.top, metrics) && !isImeVisible) {
prevInsets.top - (metrics.availableHeight - metrics.maxSheetHeight)
} else {
0
}

val newBottomInset = if (!isImeVisible) prevInsets.bottom else 0

return WindowInsetsCompat
.Builder(insets)
.setInsets(
WindowInsetsCompat.Type.navigationBars(),
Insets.of(prevInsets.left, prevInsets.top, prevInsets.right, 0),
WindowInsetsCompat.Type.systemBars(),
Insets.of(prevInsets.left, newTopInset, prevInsets.right, newBottomInset),
).build()
}

Expand Down
Loading
Loading