Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
1720448
make StackContainer a FrameLayout
kligarski Mar 11, 2026
6ede414
add skeleton classes
kligarski Mar 11, 2026
67e51c3
pass Context from host
kligarski Mar 11, 2026
8c3fb2c
debug setup
kligarski Mar 11, 2026
0938b0a
small header PoC
kligarski Mar 11, 2026
e6f7118
add temporary SFT
kligarski Mar 11, 2026
1d81436
add ability to hide the header, refactor requesting layout
kligarski Mar 12, 2026
9a65534
refactor HeaderCoordinator, handle layout for transparent header
kligarski Mar 12, 2026
e790e98
add custom shadow node
kligarski Mar 12, 2026
5ad530d
add wrapper to match Yoga layout
kligarski Mar 12, 2026
648338a
subclass ScrollingViewBehavior to synchronize content origin offset
kligarski Mar 12, 2026
37a4aef
add Pressable to SFT
kligarski Mar 12, 2026
d7b0c7b
format android
kligarski Mar 12, 2026
eb3d608
add comments
kligarski Mar 12, 2026
9f6c97c
fix build on iOS, add information to lift on scroll comment
kligarski Mar 12, 2026
abf2e32
unify naming
kligarski Mar 12, 2026
6fdd5bf
use requireContext() instead of passing it down to fragment
kligarski Mar 16, 2026
e4ac2f1
add comment informing about potential crash
kligarski Mar 16, 2026
05afaf7
format android
kligarski Mar 16, 2026
1536e9c
apply suggestion from code review
kligarski Mar 16, 2026
5f905ff
flatten native package hierarchy, drop Screen infix
kligarski Mar 18, 2026
d27fb40
add StackHeaderConfiguration and StackHeaderSubview view skeletons
kligarski Mar 18, 2026
f6668e5
header subviews WIP
kligarski Mar 20, 2026
79b928d
drop support for center subview for collapsing header
kligarski Mar 23, 2026
16ac126
add background subview
kligarski Mar 23, 2026
7b5ffb4
refactor
kligarski Mar 23, 2026
eee2d81
handle removing and changing header configuration
kligarski Mar 23, 2026
297319f
shadow state WIP
kligarski Mar 24, 2026
7c37c74
fix header configuration offset
kligarski Mar 24, 2026
00cc9ab
drop support for collapse mode pin
kligarski Mar 24, 2026
c3223c1
fix order of header subviews
kligarski Mar 24, 2026
f8d821e
handle RTL
kligarski Mar 24, 2026
a6c4591
clean up
kligarski Mar 24, 2026
a53ffc8
rename HeaderConfiguration to HeaderConfig
kligarski Apr 1, 2026
ab3d7dd
move subviews to prop in HeaderConfig instead of children
kligarski Apr 1, 2026
1b64c31
build on iOS, refactor shadow tree files
kligarski Apr 1, 2026
cfd0906
limit layout requests on header config updates
kligarski Apr 1, 2026
92c1634
styling, remove export from subview
kligarski Apr 2, 2026
7bc338f
change test to TestStackSubview
kligarski Apr 2, 2026
8019994
modify test to force container unmount on test change
kligarski Apr 2, 2026
123f660
fix dynamic changes to collapseMode for backgroundSubview
kligarski Apr 2, 2026
175b32f
force rebuild on transparent header change
kligarski Apr 2, 2026
26c08f4
fix subview positioning so that Yoga won't override it
kligarski Apr 2, 2026
7c16ee7
handle subview height dynamic updates
kligarski Apr 2, 2026
57d2c3e
remove NavigationContainer wrapper from SFT
kligarski Apr 7, 2026
1d5647e
make StackContainer a FrameLayout
kligarski Mar 11, 2026
94386c5
add skeleton classes
kligarski Mar 11, 2026
5f36f0b
pass Context from host
kligarski Mar 11, 2026
2704950
debug setup
kligarski Mar 11, 2026
faeae53
small header PoC
kligarski Mar 11, 2026
41b0635
add temporary SFT
kligarski Mar 11, 2026
5e9bc71
add ability to hide the header, refactor requesting layout
kligarski Mar 12, 2026
edf241a
refactor HeaderCoordinator, handle layout for transparent header
kligarski Mar 12, 2026
12f610e
add custom shadow node
kligarski Mar 12, 2026
e6830b8
add wrapper to match Yoga layout
kligarski Mar 12, 2026
7529b78
subclass ScrollingViewBehavior to synchronize content origin offset
kligarski Mar 12, 2026
dd8d51f
add Pressable to SFT
kligarski Mar 12, 2026
6f0e2df
format android
kligarski Mar 12, 2026
a59984c
add comments
kligarski Mar 12, 2026
61b1443
fix build on iOS, add information to lift on scroll comment
kligarski Mar 12, 2026
94772d6
unify naming
kligarski Mar 12, 2026
07ff4e2
use requireContext() instead of passing it down to fragment
kligarski Mar 16, 2026
e775daa
add comment informing about potential crash
kligarski Mar 16, 2026
5486a2f
format android
kligarski Mar 16, 2026
8b00e5b
apply suggestion from code review
kligarski Mar 16, 2026
45f5843
Merge branch '@kligarski/stack-v5-android-header-skeleton' into @klig…
kligarski Apr 7, 2026
9253695
split components
kligarski Apr 7, 2026
b560f2f
adjust SFT after split
kligarski Apr 7, 2026
6d8b897
build on iOS
kligarski Apr 8, 2026
a4b6c6b
remove old files
kligarski Apr 8, 2026
433face
add comments about the order of subviews
kligarski Apr 8, 2026
9e6ecdc
remove old comment, update class name in error message, make StackHea…
kligarski Apr 8, 2026
71dce2d
add comment explaining why we call requestApplyInsets
kligarski Apr 8, 2026
95924c1
adjust delta for ShadowStateProxy
kligarski Apr 8, 2026
1d0b7ed
refactor layout flow part 1, fix rtl workaround for collapsing header
kligarski Apr 8, 2026
5a1bff0
fix layout for RTL in small header; use Arabic titles in RTL
kligarski Apr 8, 2026
5fc00c3
minor refactors
kligarski Apr 8, 2026
9434a9a
use requestLayout instead of flag
kligarski Apr 8, 2026
9ea1b70
remove unused method
kligarski Apr 8, 2026
4ea12e3
fix shadow tree sync for transparent header, use abstraction in liste…
kligarski Apr 9, 2026
aa4f62a
Merge branch 'main' into @kligarski/stack-v5-android-header-subviews
kligarski Apr 9, 2026
9c81b74
format android
kligarski Apr 9, 2026
e91dc44
remove old test, ensure proper test naming
kligarski Apr 14, 2026
7cea3d3
Merge branch 'main' into @kligarski/stack-v5-android-header-subviews
kligarski Apr 15, 2026
d181137
rename Rtl -> RTL
kligarski Apr 16, 2026
8da4361
ensure HeaderConfig is last child of StackScreen
kligarski Apr 16, 2026
24e4228
Merge branch 'main' into @kligarski/stack-v5-android-header-subviews
kligarski Apr 16, 2026
34ed726
adjust test to new convention
kligarski Apr 16, 2026
a5e6258
Merge branch 'main' into @kligarski/stack-v5-android-header-subviews
kligarski Apr 16, 2026
390b15b
Merge branch 'main' into @kligarski/stack-v5-android-header-subviews
kligarski Apr 20, 2026
b62258e
remove unused override
kligarski Apr 20, 2026
3b3e51b
add comment in SubviewCollapseMode
kligarski Apr 20, 2026
4c34b03
bring back using clause to inherit constructor in HSComponentDescriptor
kligarski Apr 20, 2026
b6e0566
Merge branch 'main' into @kligarski/stack-v5-android-header-subviews
kligarski Apr 20, 2026
68e9553
adjust test to new scenario convention
kligarski Apr 20, 2026
6687132
fix types after enabling exactOptionalPropertyTypes TS flag
kligarski Apr 20, 2026
050aa98
keep weak ref in headerconfigproviding.onConfigChangeListener as impl…
kligarski Apr 20, 2026
5e85b14
refactor safe-stringify
kligarski Apr 20, 2026
9484906
format android
kligarski Apr 20, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.facebook.react.module.model.ReactModuleInfo
import com.facebook.react.module.model.ReactModuleInfoProvider
import com.facebook.react.uimanager.ViewManager
import com.swmansion.rnscreens.gamma.scrollviewmarker.ScrollViewMarkerViewManager
import com.swmansion.rnscreens.gamma.stack.header.config.StackHeaderConfigViewManager
import com.swmansion.rnscreens.gamma.stack.header.subview.StackHeaderSubviewViewManager
import com.swmansion.rnscreens.gamma.stack.host.StackHostViewManager
import com.swmansion.rnscreens.gamma.stack.screen.StackScreenViewManager
import com.swmansion.rnscreens.gamma.tabs.host.TabsHostViewManager
Expand Down Expand Up @@ -55,6 +57,8 @@ class RNScreensPackage : BaseReactPackage() {
StackHostViewManager(),
StackScreenViewManager(),
ScrollViewMarkerViewManager(),
StackHeaderConfigViewManager(),
StackHeaderSubviewViewManager(),
)
}

Expand Down
4 changes: 4 additions & 0 deletions android/src/main/java/com/swmansion/rnscreens/ext/ViewExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@ internal fun View.findFragmentOrNull(): Fragment? =
* before being attached to window.
*/
internal fun View.isMeasured(): Boolean = this.measuredWidth != 0 || this.measuredHeight != 0 || this.isLaidOut

internal fun View.detachFromCurrentParent() {
(parent as? ViewGroup)?.removeView(this)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.swmansion.rnscreens.gamma.common

import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.StateWrapper
import kotlin.math.abs

internal class ShadowStateProxy(
private val includesFrameSize: Boolean = true,
) {
internal var stateWrapper: StateWrapper? = null

private var lastFrameWidthInDp: Float = 0f
private var lastFrameHeightInDp: Float = 0f
private var lastContentOffsetXInDp: Float = 0f
private var lastContentOffsetYInDp: Float = 0f

fun updateStateIfNeeded(
frameWidth: Int? = null,
frameHeight: Int? = null,
contentOffsetX: Int? = null,
contentOffsetY: Int? = null,
) {
val widthInDp = frameWidth?.let { PixelUtil.toDIPFromPixel(it.toFloat()) } ?: lastFrameWidthInDp
val heightInDp = frameHeight?.let { PixelUtil.toDIPFromPixel(it.toFloat()) } ?: lastFrameHeightInDp
val offsetXInDp = contentOffsetX?.let { PixelUtil.toDIPFromPixel(it.toFloat()) } ?: lastContentOffsetXInDp
val offsetYInDp = contentOffsetY?.let { PixelUtil.toDIPFromPixel(it.toFloat()) } ?: lastContentOffsetYInDp

if (
abs(lastFrameWidthInDp - widthInDp) < DELTA &&
abs(lastFrameHeightInDp - heightInDp) < DELTA &&
abs(lastContentOffsetXInDp - offsetXInDp) < DELTA &&
abs(lastContentOffsetYInDp - offsetYInDp) < DELTA
) {
return
}

lastFrameWidthInDp = widthInDp
lastFrameHeightInDp = heightInDp
lastContentOffsetXInDp = offsetXInDp
lastContentOffsetYInDp = offsetYInDp

val map =
WritableNativeMap().apply {
if (includesFrameSize) {
putDouble("frameWidth", widthInDp.toDouble())
putDouble("frameHeight", heightInDp.toDouble())
}
putDouble("contentOffsetX", offsetXInDp.toDouble())
putDouble("contentOffsetY", offsetYInDp.toDouble())
}
stateWrapper?.updateState(map)
}

companion object {
private const val DELTA = 0.1f
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.swmansion.rnscreens.gamma.stack.screen.header
package com.swmansion.rnscreens.gamma.stack.header

import android.annotation.SuppressLint
import android.content.Context
Expand All @@ -10,13 +10,12 @@ import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.appbar.MaterialToolbar
import com.swmansion.rnscreens.gamma.stack.screen.header.configuration.StackScreenHeaderType
import com.swmansion.rnscreens.gamma.stack.header.config.StackHeaderType
import com.swmansion.rnscreens.utils.resolveDimensionAttr

internal sealed class StackScreenAppBarLayout(
internal sealed class StackHeaderAppBarLayout(
context: Context,
) : AppBarLayout(context) {
abstract val toolbar: MaterialToolbar
Expand All @@ -36,14 +35,13 @@ internal sealed class StackScreenAppBarLayout(

internal class Small(
context: Context,
) : StackScreenAppBarLayout(context) {
) : StackHeaderAppBarLayout(context) {
override val toolbar =
MaterialToolbar(context).apply {
elevation = 0f
layoutParams =
LayoutParams(MATCH_PARENT, WRAP_CONTENT).apply {
// TODO: debug only for small header, must be moved to configuration
// scrollFlags = SCROLL_FLAG_SCROLL or SCROLL_FLAG_SNAP
// TODO: debug only for small header, must be moved to config
scrollFlags = SCROLL_FLAG_NO_SCROLL
}
}
Expand All @@ -56,17 +54,8 @@ internal sealed class StackScreenAppBarLayout(
@SuppressLint("ViewConstructor")
internal class Collapsing(
context: Context,
val type: StackScreenHeaderType,
) : StackScreenAppBarLayout(context) {
init {
require(
type == StackScreenHeaderType.MEDIUM ||
type == StackScreenHeaderType.LARGE,
) {
"[RNScreens] Collapsing StackScreenAppBarLayout must be MEDIUM or LARGE type."
}
}

val type: StackHeaderType,
) : StackHeaderAppBarLayout(context) {
override val toolbar =
MaterialToolbar(context).apply {
elevation = 0f
Expand All @@ -80,13 +69,13 @@ internal sealed class StackScreenAppBarLayout(
}
}

val collapsingToolbarLayout: CollapsingToolbarLayout =
internal val collapsingToolbarLayout: CollapsingToolbarLayout =
run {
val (styleAttr, sizeAttr) =
when (type) {
StackScreenHeaderType.MEDIUM ->
StackHeaderType.MEDIUM ->
Pair(R.attr.collapsingToolbarLayoutMediumStyle, R.attr.collapsingToolbarLayoutMediumSize)
StackScreenHeaderType.LARGE ->
StackHeaderType.LARGE ->
Pair(R.attr.collapsingToolbarLayoutLargeStyle, R.attr.collapsingToolbarLayoutLargeSize)
else -> error("[RNScreens] Invalid header mode.")
}
Expand All @@ -97,27 +86,32 @@ internal sealed class StackScreenAppBarLayout(
MATCH_PARENT,
resolveDimensionAttr(context, sizeAttr),
).apply {
// TODO: debug only for medium/large header, must be moved to configuration
scrollFlags = SCROLL_FLAG_SCROLL or SCROLL_FLAG_EXIT_UNTIL_COLLAPSED or SCROLL_FLAG_SNAP
// scrollFlags = SCROLL_FLAG_NO_SCROLL
// TODO: debug only for medium/large header, must be moved to config
scrollFlags = SCROLL_FLAG_SCROLL or SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
}
addView(toolbar)
}
}

init {
require(
type == StackHeaderType.MEDIUM ||
type == StackHeaderType.LARGE,
) {
"[RNScreens] Collapsing StackHeaderAppBarLayout must be MEDIUM or LARGE type."
}
addView(collapsingToolbarLayout)
}
}

companion object {
fun create(
context: Context,
type: StackScreenHeaderType,
): StackScreenAppBarLayout =
type: StackHeaderType,
): StackHeaderAppBarLayout =
when (type) {
StackScreenHeaderType.SMALL -> Small(context)
StackScreenHeaderType.MEDIUM, StackScreenHeaderType.LARGE -> Collapsing(context, type)
StackHeaderType.SMALL -> Small(context)
StackHeaderType.MEDIUM, StackHeaderType.LARGE -> Collapsing(context, type)
}
}
}
Loading
Loading