Skip to content

fix(Android, FormSheet): Add fallbacks for unblocking sheet transition#4053

Open
t0maboro wants to merge 5 commits into
mainfrom
@t0maboro/formsheet-transition-coordinator-fallback
Open

fix(Android, FormSheet): Add fallbacks for unblocking sheet transition#4053
t0maboro wants to merge 5 commits into
mainfrom
@t0maboro/formsheet-transition-coordinator-fallback

Conversation

@t0maboro
Copy link
Copy Markdown
Contributor

@t0maboro t0maboro commented May 18, 2026

Description

Currently, the FormSheet enter transition relies on two signals to start:

  • The screen container being laid out
  • WindowInsets being dispatched to the listener attached in ScreenStackFragment.

On devices running API 34 and below in brownfield setups, the bottom sheet transition may hang indefinitely. This happens because the WindowInsets are consumed higher up in the view hierarchy and never reach our listener.


The difference in behavior comes down to how system window insets are handled by the PhoneWindow and ViewRootImpl before and after edge-to-edge enforcement.


On API 34 and below:

During Activity.onCreate the PhoneWindow.generateLayout is called for the native hierarchy setup, the default behavior is to fit the content to system windows (mDecorFitsSystemWindows = true).

When DecorView is attached to the window, applyDecorFitsSystemWindows attaches a default insets listener (sDefaultContentInsetsApplier) to ViewRootImpl.

When ViewRootImpl dispatches insets via computeSystemWindowInsets, the sDefaultContentInsetsApplier calculates the required paddings and consumes the insets. As a result, the LinearLayout below the DecorView doesn't propagate insets down to our ScreenContainer.


On API 36:

With enforced edge-to-edge, PhoneWindow.generateLayout explicitly sets mDecorFitsSystemWindows = false under the hood.

Because of this, applyDecorFitsSystemWindows passes null to ViewRootImpl instead of the default applier.

In computeSystemWindowInsets, since the listener is null, the insets are not consumed and are simply passed down the view hierarchy, successfully reaching our ScreenContainer and unblocking the transition.


Proposed Solution:

  • Since we cannot guarantee that WindowInsets will ever reach our view, I'm introducing a fallback mechanism. If the insets are not received within an arbitrary time, I assume they were consumed upstream, and I should trigger the transition. If insets arrive, the fallback is cancelled.
  • OnLayoutChangeListener only catches future layout passes. We now check if (isLaidOut && width > 0 && height > 0) immediately after attaching the listener. If the view is already laid out, we manually trigger the onScreenContainerLayoutChanged callback to prevent the coordinator from hanging from this path.

Closes: #4010

Changes

  • timeout fallback in BottomSheetTransitionCoordinator regarding insets
  • synchronous path for triggering layout callback for BottomSheetTransitionCoordinator rather than relying on the listener

Before & after - visual documentation

Before After
before.mov
after.mov

Test plan

  • Brownfield setup with patch: https://github.com/t0maboro/RNS4010
  • Test3336 - ensuring that the top inset is respected relatively to the sheetShouldOverflowTopInset prop value

Checklist

  • Included code example that can be used to test this change.
  • For visual changes, included screenshots / GIFs / recordings documenting the change.
  • For API changes, updated relevant public types.
  • Ensured that CI passes

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Android FormSheet transition fallbacks so postponed enter transitions are unblocked when layout or WindowInsets callbacks are missed in brownfield/API <=34 setups.

Changes:

  • Cancels pending bottom-sheet transition fallback work during onDestroyView.
  • Adds an immediate “already laid out” path after attaching the layout listener.
  • Adds a delayed insets fallback in BottomSheetTransitionCoordinator.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt Wires lifecycle cancellation and immediate layout readiness notification for FormSheet transitions.
android/src/main/java/com/swmansion/rnscreens/bottomsheet/BottomSheetTransitionCoordinator.kt Adds main-thread timeout fallback for missing insets dispatch.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

Comment thread android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt Outdated
t0maboro and others added 2 commits May 18, 2026 13:16
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

formSheet modal is invisible in brownfield Fabric apps until re-render because postponed enter transition never starts

2 participants