fix(ScreenContainer): prevent blank-screen flash during rapid tab switching with animation#3874
Open
steve228uk wants to merge 3 commits intosoftware-mansion:mainfrom
Open
fix(ScreenContainer): prevent blank-screen flash during rapid tab switching with animation#3874steve228uk wants to merge 3 commits intosoftware-mansion:mainfrom
steve228uk wants to merge 3 commits intosoftware-mansion:mainfrom
Conversation
…nactive When `activityState` is driven by an `Animated.Value` interpolation (as react-navigation does for tab animations), each screen's state is updated frame-by-frame and independently. During rapid tab switching there is a brief window where ALL screens cross the inactive threshold simultaneously: the leaving screen's animated value has already dropped below 1.0 while the arriving screen's has not yet risen above 1.0. Previously, the leaving screen was detached immediately upon reaching `activityState == 0`, even if no other screen was active or transitioning. This left the container with no visible screen for one or more frames, producing a blank-screen flash — the bug reported in react-navigation/react-navigation#12755. The fix: only detach an in-tree inactive screen when at least one other screen is still active or transitioning, guaranteeing something remains visible until the arriving screen takes over. Screens that have been removed from the React tree entirely (orphaned) are always detached immediately since they can never become active again. Applied to both iOS (`RNSScreenContainer.mm`) and Android (`ScreenContainer.kt`) for consistency. https://claude.ai/code/session_01HSsJnvYwm3gHfeH27CXRsW
…, trim comments Rename the guard variable to better reflect its intent (something must remain visible before we detach), and shorten the explanatory comments to drop the self-evident orphan-handling sentence. https://claude.ai/code/session_01HSsJnvYwm3gHfeH27CXRsW
…ing-KsQga fix(ScreenContainer): defer detachment when all screens transiently inactive
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.
Description
When
detachInactiveScreensis enabled and a tab navigator uses theanimationprop, rapidly switching between tabs can produce a brief blank screen on iOS (and potentially Android).The root cause is a timing race in how
activityStateis updated. React-navigation drivesactivityStatevia anAnimated.Valueinterpolation, so each screen's value updates independently, frame by frame. During rapid tab switching there is a transient window where all screens simultaneously reportactivityState == 0: the leaving screen's animated value has already dropped below the1.0threshold while the arriving screen's value has not yet risen above it.updateContainer/onUpdatewas detaching the leaving screen immediately upon seeingactivityState == 0, before any other screen became visible — leaving the container empty for one or more frames.The fix guards detachment of in-tree inactive screens: a screen is only detached when at least one other screen is still active or transitioning, guaranteeing something remains visible until the arriving screen takes over. Screens removed from the React tree entirely (orphaned) are still detached immediately.
Closes #3824
Related upstream report: react-navigation/react-navigation#12755
Changes
ios/RNSScreenContainer.mm— added ahasVisibleScreenpre-check inupdateContainerbefore the inactive-screen detach loopandroid/…/ScreenContainer.kt— same guard added inonUpdatefor consistencyTest plan
Steps to reproduce (before this fix):
animationset (e.g.animation="shift"oranimation="fade")detachInactiveScreensis enabled (it is by default)After this fix: switching rapidly between tabs no longer produces a blank screen. The leaving screen stays mounted until the arriving screen begins its transition.
Workaround (before this fix): set
detachInactiveScreens={false}on the navigator.No new test files were added. This can be manually verified using the reproduction in react-navigation/react-navigation#12755 or with a minimal snack using
@react-navigation/bottom-tabs+animationprop.Patch
Checklist