fix(android): keep an open sheet interactive with a transparent scrim - #82
Open
giaBaoJS wants to merge 1 commit into
Open
Conversation
The host derived its interactivity from the scrim's resolved opacity, so a modal sheet whose scrimOpacities are 0 at the open detent reported itself non-interactive once it settled. In nativeOverlay mode that adds FLAG_NOT_TOUCHABLE to the dialog window, and drops its alpha to 0 when there is no onCloseRequest handler, so an open sheet stops receiving touches or disappears outright. Follow the resolved target detent and the on-screen sheet height instead, matching iOS, where the sheet stays hit-testable and only the scrim area follows scrim visibility.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
On Android the host's interactivity is derived from the scrim's resolved
opacity:
scrimProgresscomes straight from the publicscrimOpacitiesprop, so amodal sheet configured with no dim reports itself non-interactive the moment
it finishes opening.
updateInteractionState()then puts the host intoBOX_NONEand, innativeOverlaymode,OverlayCloseRequestControlleradds
FLAG_NOT_TOUCHABLEto the dialog window and drops its alpha to 0 whenthere is no
onCloseRequesthandler.It is sticky: recovering needs another
emitPosition(), and the touch-drivenproducers of one are exactly what the flag just disabled.
Reproduction
Set
indexto 1 on Android. The sheet animates in (interactive while thespring runs) and then vanishes at settle, because the overlay window goes to
alpha 0. Add an
onCloseRequesthandler and it stays visible but ignoresevery touch instead. The same happens for any
scrimOpacitiesentry that is0 at an open detent, for example
[0, 0, 1]withdetents={[0, 200, 600]}.Root cause
Scrim opacity is presentation styling, not a signal that the sheet is on
screen, so a legal
scrimOpacitiesvalue silently switched off input for anopen sheet. iOS does not share this:
point(inside:)returns true forpresentedSheetFrameunconditionally and only the area outside the sheet isgated on
isScrimVisible.This tracks the resolved target detent and the on-screen sheet height
instead. With the default opacities (0 at closed detents, 1 otherwise) the
predicate is equivalent to the previous one, so only the transparent-scrim
configurations change. Scrim taps keep following scrim visibility, which is
what iOS does too.
Test
open native overlay stays interactive with a fully transparent scrimopensa
nativeOverlaysheet at its 300pt detent withscrimOpacitiesof[0f, 0f]and asserts the dialog window is touchable and at alpha 1. Withoutthe source change it fails with
expected:<0> but was:<16>(
FLAG_NOT_TOUCHABLE).animateInis false in the fixture, so nothing inthe test depends on an animation completing.
./example/android/gradlew --project-dir example/android :swmansion_react-native-bottom-sheet:testDebugUnitTest --rerun-tasksgoes from 65 to 66 tests, 0 failures.
bun run typecheckandbun run lintare clean.