Skip to content
Open
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
71 changes: 64 additions & 7 deletions android/src/main/java/com/reactnativepagerview/ComposePagerView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,43 @@ class ComposePagerView(context: Context) : FrameLayout(context) {
private val composeLifecycleOwner = ComposeViewLifecycleOwner()
private var composeView: ComposeView? = null
private val pages = mutableStateListOf<View>()
// RN's logical children must update immediately, but screens uses
// startViewTransition to keep the old native hierarchy drawing during pop.
// Compose must keep the same page hosts alive for that interval too.
private val transitionPages = mutableStateOf<List<View>?>(null)
private var dropRequested = false
private var disposed = false

override fun startViewTransition(view: View) {
super.startViewTransition(view)
if (view === composeView && transitionPages.value == null && !disposed) {
transitionPages.value = pages.toList()
}
}

override fun endViewTransition(view: View) {
super.endViewTransition(view)
if (view === composeView) {
finishPageTransition()
}
}

private fun finishPageTransition() {
val retained = transitionPages.value ?: return
if (dropRequested) {
disposeNow()
}
transitionPages.value = null
retained.filterNot { it in pages }.forEach { view ->
(view.parent as? ViewGroup)?.removeView(view)
}
}

private fun removePageView(view: View) {
if (transitionPages.value?.contains(view) != true) {
(view.parent as? ViewGroup)?.removeView(view)
}
}
private val scrollEnabledState = mutableStateOf(true)
private val orientationState = mutableStateOf(Orientation.Horizontal)
private val layoutDirectionState = mutableStateOf(LayoutDirection.Ltr)
Expand Down Expand Up @@ -130,10 +167,23 @@ class ComposePagerView(context: Context) : FrameLayout(context) {
// dispose the composition, since DisposeOnLifecycleDestroyed only acts
// on ON_DESTROY.
composeLifecycleOwner.detach()
// Detach is the fallback when the parent does not deliver endViewTransition.
finishPageTransition()
if (dropRequested) disposeNow()
super.onDetachedFromWindow()
}

fun dispose() {
dropRequested = true
if (isAttachedToWindow && transitionPages.value != null) {
return
}
disposeNow()
}

private fun disposeNow() {
if (disposed) return
disposed = true
composeLifecycleOwner.destroy()
}

Expand Down Expand Up @@ -242,15 +292,15 @@ class ComposePagerView(context: Context) : FrameLayout(context) {
return
}
val view = pages.removeAt(index)
(view.parent as? ViewGroup)?.removeView(view)
removePageView(view)
}

fun removeAllPages() {
pages.forEach { view ->
(view.parent as? ViewGroup)?.removeView(view)
removePageView(view)
}
pages.clear()
resetPagerState()
if (transitionPages.value == null) resetPagerState()
}

private fun resetPagerState() {
Expand Down Expand Up @@ -429,6 +479,7 @@ class ComposePagerView(context: Context) : FrameLayout(context) {
}

private fun dispatchPageSelected(position: Int) {
if (dropRequested) return
if (lastEmittedPageSelected == position) {
return
}
Expand All @@ -439,12 +490,14 @@ class ComposePagerView(context: Context) : FrameLayout(context) {
}

private fun dispatchPageScroll(position: Int, offset: Float) {
if (dropRequested) return
UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)?.dispatchEvent(
PageScrollEvent(id, position, offset)
)
}

private fun dispatchScrollState(state: String) {
if (dropRequested) return
if (lastEmittedScrollState == state) {
return
}
Expand All @@ -456,13 +509,17 @@ class ComposePagerView(context: Context) : FrameLayout(context) {

@Composable
private fun PagerContent() {
val pageCount = pages.size
// Lazy page content can run after RN mutates the logical child list but
// before this composition is replaced. Capture an immutable snapshot so
// an existing item provider never indexes a list that has since shrunk.
val displayedPages = transitionPages.value ?: pages.toList()
val pageCount = displayedPages.size
if (pageCount == 0) {
return
}

val pagerState = rememberPagerState(initialPage = initialPage.coerceIn(0, pageCount - 1)) {
pages.size
pageCount
}

LaunchedEffect(pageCount) {
Expand Down Expand Up @@ -564,7 +621,7 @@ class ComposePagerView(context: Context) : FrameLayout(context) {
reverseLayout = false,
beyondViewportPageCount = beyondViewportPageCount
) { page ->
PageHost(pages[page])
PageHost(displayedPages[page])
}
} else {
HorizontalPager(
Expand All @@ -575,7 +632,7 @@ class ComposePagerView(context: Context) : FrameLayout(context) {
reverseLayout = false,
beyondViewportPageCount = beyondViewportPageCount
) { page ->
PageHost(pages[page])
PageHost(displayedPages[page])
}
}
}
Expand Down