Skip to content

fix(android): give ComposeView its own Lifecycle to fix #1103 and #1104 - #1105

Merged
troZee merged 4 commits into
callstack:masterfrom
GaelCO:fix/fix-1103-1104
Aug 31, 2026
Merged

fix(android): give ComposeView its own Lifecycle to fix #1103 and #1104#1105
troZee merged 4 commits into
callstack:masterfrom
GaelCO:fix/fix-1103-1104

Conversation

@GaelCO

@GaelCO GaelCO commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #1103
Fixes #1104

Test plan

  • Manually verified with a react-navigation material-top-tabs stack that covering/revealing a screen containing the pager no longer blanks it out.
  • Manually verified a FlatList page inside the pager keeps its scroll position after the screen is covered and revealed again.

…and callstack#1104

react-native-screens fully removes and re-adds a covered screen's Fragment
instead of just hiding it, destroying its view-tree Lifecycle. Compose's
default disposal strategy keys off that ambient Lifecycle, so the pager's
ComposeView was torn down and rebuilt on every cover/reveal cycle, causing
a blank pager (callstack#1103) and loss of page state such as FlatList scroll
position (callstack#1104). Giving the ComposeView its own self-owned Lifecycle lets
the composition survive the cycle untouched.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes Android regressions in the Compose-based pager when screens are covered/revealed (notably with react-native-screens) by decoupling the ComposeView’s composition disposal from the ambient Fragment view-tree Lifecycle, keeping the composition (and hosted native page views) alive across cover/reveal cycles.

Changes:

  • Add explicit cleanup in onDropViewInstance to destroy a self-owned Lifecycle and dispose the Compose composition.
  • Rework ComposePagerView to create/bind a ComposeView with a dedicated LifecycleOwner and DisposeOnLifecycleDestroyed strategy.
  • Add an AndroidX Lifecycle runtime dependency required for view-tree lifecycle owner APIs.

Reviewed changes

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

File Description
android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt Disposes pager resources when the RN view instance is dropped.
android/src/main/java/com/reactnativepagerview/ComposePagerView.kt Introduces a self-owned lifecycle for the ComposeView and adjusts attach/detach behavior to preserve composition across cover/reveal.
android/build.gradle Adds AndroidX Lifecycle runtime dependency for the new lifecycle owner usage.
Suppressed comments (1)

android/src/main/java/com/reactnativepagerview/ComposePagerView.kt:652

  • ComposeViewLifecycleOwner is hard-coded to start in Lifecycle.State.RESUMED and stay there until DESTROYED. That can keep lifecycle-aware work (e.g., coroutines/flows started with lifecycle-aware APIs) running even while the view is detached/covered, which is likely more work than necessary. Consider starting at CREATED and adding explicit attach/detach state transitions so the composition can pause while offscreen without being disposed.
// A Lifecycle that isn't derived from the ambient Fragment/Activity: it
// starts RESUMED and only moves to DESTROYED when destroy() is called
// explicitly, so it survives being covered/revealed by react-native-screens'
// Fragment remove+re-add (see the composeLifecycleOwner field comment on
// ComposePagerView above).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +116 to +120
override fun onDetachedFromWindow() {
updateSameOrientationAncestorsGestureState(false)
if (composeView.parent === this) {
super.removeView(composeView)
didSetContent = false
}
// composeView is intentionally left attached and alive here: its
// Lifecycle is self-owned (see composeLifecycleOwner) and only reaches
// DESTROYED in dispose(), so there is nothing to tear down on a mere
Comment thread android/build.gradle Outdated
Comment on lines +249 to +251
// Needed for ComposeViewLifecycleOwner (see ComposePagerView.kt), which
// gives the ComposeView a self-owned Lifecycle instead of the ambient one.
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1"

Copilot AI left a comment

Copy link
Copy Markdown

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 4 out of 4 changed files in this pull request and generated no new comments.

@troZee

troZee commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

@GaelCO
Since v9, Android PagerView uses Jetpack Compose. When a native-stack screen covers a screen containing PagerView, react-native-screens removes and later recreates the Fragment’s view tree. This destroys the old view-tree LifecycleOwner.

If ComposeView uses that lifecycle directly, its composition is disposed or becomes unusable, resulting in the blank pager reported in #1103. Recreating the entire ComposeView fixes the blank state, but also recreates every page’s native host, causing state such as FlatList scroll position to be lost (#1104).

The fix gives ComposeView a stable, pager-owned lifecycle. It:

  • follows the active host through CREATED, STARTED, and RESUMED;
  • moves back to CREATED when detached;
  • ignores destruction of a temporary Fragment view lifecycle;
  • reconnects to the replacement lifecycle after reattachment;
  • reaches DESTROYED only when React Native actually drops the PagerView.

This is also an improvement over the previous implementation, which treated every attached view as RESUMED. A view can remain attached while its Activity or Fragment is paused or stopped, so lifecycle-aware content could otherwise keep running in the background.

I ran the new ComposeViewLifecycleOwnerTest suite successfully.

Before
https://github.com/user-attachments/assets/c112820a-523c-412d-8d23-6efb95cb628a

After
https://github.com/user-attachments/assets/ce8e478e-0a98-4386-9bdd-965969187374

@troZee
troZee merged commit f8b5883 into callstack:master Aug 31, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants