react-native-screens 4.19.0-nightly-20251204-1746a584e → 4.19.0-nightly-20251206-910978b9a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/CustomAppBarLayout.kt +25 -0
- package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +32 -0
- package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +24 -1
- package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +18 -9
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +2 -2
- package/android/src/main/java/com/swmansion/rnscreens/utils/DecorViewInsetsUtils.kt +23 -0
- package/android/src/main/java/com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper.kt +6 -2
- package/package.json +9 -9
package/android/build.gradle
CHANGED
|
@@ -220,7 +220,7 @@ dependencies {
|
|
|
220
220
|
implementation 'androidx.fragment:fragment-ktx:1.6.1'
|
|
221
221
|
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
|
222
222
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
|
223
|
-
implementation 'com.google.android.material:material:1.
|
|
223
|
+
implementation 'com.google.android.material:material:1.13.0'
|
|
224
224
|
implementation "androidx.core:core-ktx:1.8.0"
|
|
225
225
|
|
|
226
226
|
constraints {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package com.swmansion.rnscreens
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import com.google.android.material.appbar.AppBarLayout
|
|
6
|
+
|
|
7
|
+
@SuppressLint("ViewConstructor")
|
|
8
|
+
class CustomAppBarLayout(
|
|
9
|
+
context: Context,
|
|
10
|
+
) : AppBarLayout(context) {
|
|
11
|
+
/**
|
|
12
|
+
* Handles the layout correction from the child Toolbar.
|
|
13
|
+
*/
|
|
14
|
+
internal fun applyToolbarLayoutCorrection(toolbarPaddingTop: Int) {
|
|
15
|
+
applyFrameCorrectionByTopInset(toolbarPaddingTop)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private fun applyFrameCorrectionByTopInset(topInset: Int) {
|
|
19
|
+
measure(
|
|
20
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
21
|
+
MeasureSpec.makeMeasureSpec(height + topInset, MeasureSpec.EXACTLY),
|
|
22
|
+
)
|
|
23
|
+
layout(left, top, right, bottom + topInset)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -34,11 +34,31 @@ open class CustomToolbar(
|
|
|
34
34
|
|
|
35
35
|
private val shouldApplyTopInset = true
|
|
36
36
|
|
|
37
|
+
private var shouldApplyLayoutCorrectionForTopInset = false
|
|
38
|
+
|
|
37
39
|
private var lastInsets = InsetsCompat.NONE
|
|
38
40
|
|
|
39
41
|
private var isForceShadowStateUpdateOnLayoutRequested = false
|
|
40
42
|
|
|
41
43
|
private var isLayoutEnqueued = false
|
|
44
|
+
|
|
45
|
+
init {
|
|
46
|
+
// Ensure ActionMenuView is initialized as soon as the Toolbar is created.
|
|
47
|
+
//
|
|
48
|
+
// Android measures Toolbar height based on the tallest child view.
|
|
49
|
+
// During the first measurement:
|
|
50
|
+
// 1. The Toolbar is created but not yet added to the action bar via `activity.setSupportActionBar(toolbar)`
|
|
51
|
+
// (typically called in `onUpdate` method from `ScreenStackHeaderConfig`).
|
|
52
|
+
// 2. At this moment, the title view may exist, but ActionMenuView (which may be taller) hasn't been added yet.
|
|
53
|
+
// 3. This causes the initial height calculation to be based on the title view, potentially too small.
|
|
54
|
+
// 4. When ActionMenuView is eventually attached, the Toolbar might need to re-layout due to the size change.
|
|
55
|
+
//
|
|
56
|
+
// By referencing the menu here, we trigger `ensureMenu`, which creates and attaches ActionMenuView early.
|
|
57
|
+
// This guarantees that all size-dependent children are present during the first layout pass,
|
|
58
|
+
// resulting in correct height determination from the beginning.
|
|
59
|
+
menu
|
|
60
|
+
}
|
|
61
|
+
|
|
42
62
|
private val layoutCallback: Choreographer.FrameCallback =
|
|
43
63
|
object : Choreographer.FrameCallback {
|
|
44
64
|
override fun doFrame(frameTimeNanos: Long) {
|
|
@@ -55,6 +75,17 @@ open class CustomToolbar(
|
|
|
55
75
|
|
|
56
76
|
override fun requestLayout() {
|
|
57
77
|
super.requestLayout()
|
|
78
|
+
|
|
79
|
+
val maybeAppBarLayout = parent as? CustomAppBarLayout
|
|
80
|
+
maybeAppBarLayout?.let {
|
|
81
|
+
if (shouldApplyLayoutCorrectionForTopInset && !it.isInLayout) {
|
|
82
|
+
// In `applyToolbarLayoutCorrection`, we call and immediate layout on AppBarLayout
|
|
83
|
+
// to update it right away and avoid showing a potentially wrong UI state.
|
|
84
|
+
it.applyToolbarLayoutCorrection(paddingTop)
|
|
85
|
+
shouldApplyLayoutCorrectionForTopInset = false
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
58
89
|
val softInputMode =
|
|
59
90
|
(context as ThemedReactContext)
|
|
60
91
|
.currentActivity
|
|
@@ -161,6 +192,7 @@ open class CustomToolbar(
|
|
|
161
192
|
right: Int,
|
|
162
193
|
bottom: Int,
|
|
163
194
|
) {
|
|
195
|
+
shouldApplyLayoutCorrectionForTopInset = true
|
|
164
196
|
requestForceShadowStateUpdateOnLayout()
|
|
165
197
|
setPadding(left, top, right, bottom)
|
|
166
198
|
}
|
|
@@ -8,6 +8,7 @@ import android.util.SparseArray
|
|
|
8
8
|
import android.view.MotionEvent
|
|
9
9
|
import android.view.View
|
|
10
10
|
import android.view.ViewGroup
|
|
11
|
+
import android.view.WindowInsets
|
|
11
12
|
import android.view.WindowManager
|
|
12
13
|
import android.webkit.WebView
|
|
13
14
|
import android.widget.ImageView
|
|
@@ -35,6 +36,7 @@ import com.swmansion.rnscreens.events.SheetDetentChangedEvent
|
|
|
35
36
|
import com.swmansion.rnscreens.ext.asScreenStackFragment
|
|
36
37
|
import com.swmansion.rnscreens.ext.parentAsViewGroup
|
|
37
38
|
import com.swmansion.rnscreens.gamma.common.FragmentProviding
|
|
39
|
+
import com.swmansion.rnscreens.utils.getDecorViewTopInset
|
|
38
40
|
import kotlin.math.max
|
|
39
41
|
|
|
40
42
|
@SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated.
|
|
@@ -52,6 +54,8 @@ class Screen(
|
|
|
52
54
|
val reactEventDispatcher: EventDispatcher?
|
|
53
55
|
get() = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
54
56
|
|
|
57
|
+
var insetsApplied = false
|
|
58
|
+
|
|
55
59
|
var fragmentWrapper: ScreenFragmentWrapper? = null
|
|
56
60
|
var container: ScreenContainer? = null
|
|
57
61
|
var activityState: ActivityState? = null
|
|
@@ -191,7 +195,20 @@ class Screen(
|
|
|
191
195
|
val width = r - l
|
|
192
196
|
val height = b - t
|
|
193
197
|
|
|
194
|
-
|
|
198
|
+
if (!insetsApplied && headerConfig?.isHeaderHidden == false && headerConfig?.isHeaderTranslucent == false) {
|
|
199
|
+
val topLevelDecorView =
|
|
200
|
+
requireNotNull(
|
|
201
|
+
reactContext.currentActivity?.window?.decorView,
|
|
202
|
+
) { "[RNScreens] DecorView is required for applying inset correction, but was null." }
|
|
203
|
+
|
|
204
|
+
val topInset = getDecorViewTopInset(topLevelDecorView)
|
|
205
|
+
val correctedHeight = height - topInset
|
|
206
|
+
val correctedOffsetY = t + topInset
|
|
207
|
+
|
|
208
|
+
dispatchShadowStateUpdate(width, correctedHeight, correctedOffsetY)
|
|
209
|
+
} else {
|
|
210
|
+
dispatchShadowStateUpdate(width, height, t)
|
|
211
|
+
}
|
|
195
212
|
}
|
|
196
213
|
}
|
|
197
214
|
|
|
@@ -502,6 +519,12 @@ class Screen(
|
|
|
502
519
|
}
|
|
503
520
|
}
|
|
504
521
|
|
|
522
|
+
override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets? {
|
|
523
|
+
insetsApplied = true
|
|
524
|
+
|
|
525
|
+
return super.onApplyWindowInsets(insets)
|
|
526
|
+
}
|
|
527
|
+
|
|
505
528
|
override fun onAttachedToWindow() {
|
|
506
529
|
super.onAttachedToWindow()
|
|
507
530
|
|
|
@@ -411,18 +411,27 @@ open class ScreenContainer(
|
|
|
411
411
|
// attach newly activated screens
|
|
412
412
|
var addedBefore = false
|
|
413
413
|
val pendingFront: ArrayList<ScreenFragmentWrapper> = ArrayList()
|
|
414
|
-
|
|
415
414
|
for (fragmentWrapper in screenWrappers) {
|
|
415
|
+
fragmentWrapper.screen.setTransitioning(transitioning)
|
|
416
|
+
|
|
416
417
|
val activityState = getActivityState(fragmentWrapper)
|
|
417
|
-
if (activityState
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
418
|
+
if (activityState == ActivityState.INACTIVE) {
|
|
419
|
+
continue
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (fragmentWrapper.fragment.isAdded) {
|
|
423
|
+
if (addedBefore) {
|
|
424
|
+
detachScreen(it, fragmentWrapper.fragment)
|
|
425
|
+
pendingFront.add(fragmentWrapper)
|
|
426
|
+
}
|
|
427
|
+
} else {
|
|
428
|
+
if (addedBefore) {
|
|
429
|
+
pendingFront.add(fragmentWrapper)
|
|
430
|
+
} else {
|
|
431
|
+
addedBefore = true
|
|
432
|
+
attachScreen(it, fragmentWrapper.fragment)
|
|
433
|
+
}
|
|
424
434
|
}
|
|
425
|
-
fragmentWrapper.screen.setTransitioning(transitioning)
|
|
426
435
|
}
|
|
427
436
|
|
|
428
437
|
for (screenFragment in pendingFront) {
|
|
@@ -52,7 +52,7 @@ class KeyboardVisible(
|
|
|
52
52
|
class ScreenStackFragment :
|
|
53
53
|
ScreenFragment,
|
|
54
54
|
ScreenStackFragmentWrapper {
|
|
55
|
-
private var appBarLayout:
|
|
55
|
+
private var appBarLayout: CustomAppBarLayout? = null
|
|
56
56
|
private var toolbar: Toolbar? = null
|
|
57
57
|
private var isToolbarShadowHidden = false
|
|
58
58
|
private var isToolbarTranslucent = false
|
|
@@ -204,7 +204,7 @@ class ScreenStackFragment :
|
|
|
204
204
|
|
|
205
205
|
if (!screen.usesFormSheetPresentation()) {
|
|
206
206
|
appBarLayout =
|
|
207
|
-
context?.let {
|
|
207
|
+
context?.let { CustomAppBarLayout(it) }?.apply {
|
|
208
208
|
// By default AppBarLayout will have a background color set but since we cover the whole layout
|
|
209
209
|
// with toolbar (that can be semi-transparent) the bar layout background color does not pay a
|
|
210
210
|
// role. On top of that it breaks screens animations when alfa offscreen compositing is off
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package com.swmansion.rnscreens.utils
|
|
2
|
+
|
|
3
|
+
import android.view.View
|
|
4
|
+
import androidx.core.view.ViewCompat
|
|
5
|
+
import androidx.core.view.WindowInsetsCompat
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Retrieves the top system inset (such as status bar or display cutout) from the given decor view.
|
|
9
|
+
*
|
|
10
|
+
* @param decorView The top-level window decor view.
|
|
11
|
+
* @return The top inset in pixels.
|
|
12
|
+
*/
|
|
13
|
+
internal fun getDecorViewTopInset(decorView: View): Int {
|
|
14
|
+
val insetsCompat = ViewCompat.getRootWindowInsets(decorView) ?: return 0
|
|
15
|
+
|
|
16
|
+
return getTopInset(insetsCompat)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private fun getTopInset(insetsCompat: WindowInsetsCompat): Int =
|
|
20
|
+
insetsCompat
|
|
21
|
+
.getInsets(
|
|
22
|
+
WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout(),
|
|
23
|
+
).top
|
|
@@ -179,8 +179,9 @@ internal class ScreenDummyLayoutHelper(
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
val topLevelDecorView = requireActivity().window.decorView
|
|
182
|
+
val topInset = getDecorViewTopInset(topLevelDecorView)
|
|
182
183
|
|
|
183
|
-
// These dimensions are not accurate, as they do include
|
|
184
|
+
// These dimensions are not accurate, as they do include navigation bar, however
|
|
184
185
|
// it is ok for our purposes.
|
|
185
186
|
val decorViewWidth = topLevelDecorView.width
|
|
186
187
|
val decorViewHeight = topLevelDecorView.height
|
|
@@ -208,7 +209,10 @@ internal class ScreenDummyLayoutHelper(
|
|
|
208
209
|
// scenarios when layout violates measured dimensions.
|
|
209
210
|
coordinatorLayout.layout(0, 0, decorViewWidth, decorViewHeight)
|
|
210
211
|
|
|
211
|
-
|
|
212
|
+
// Include the top inset to account for the extra padding manually applied to the CustomToolbar.
|
|
213
|
+
val totalAppBarLayoutHeight = appBarLayout.height.toFloat() + topInset
|
|
214
|
+
|
|
215
|
+
val headerHeight = PixelUtil.toDIPFromPixel(totalAppBarLayoutHeight)
|
|
212
216
|
cache = CacheEntry(CacheKey(fontSize, isTitleEmpty), headerHeight)
|
|
213
217
|
return headerHeight
|
|
214
218
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-screens",
|
|
3
|
-
"version": "4.19.0-nightly-
|
|
3
|
+
"version": "4.19.0-nightly-20251206-910978b9a",
|
|
4
4
|
"description": "Native navigation primitives for your React Native app.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"submodules": "git submodule update --init --recursive && (cd react-navigation && yarn && yarn build && cd ../)",
|
|
@@ -86,14 +86,14 @@
|
|
|
86
86
|
"@react-native-community/cli": "20.0.1",
|
|
87
87
|
"@react-native-community/cli-platform-android": "20.0.0",
|
|
88
88
|
"@react-native-community/cli-platform-ios": "20.0.0",
|
|
89
|
-
"@react-native/babel-preset": "0.
|
|
90
|
-
"@react-native/eslint-config": "0.
|
|
91
|
-
"@react-native/metro-config": "0.
|
|
92
|
-
"@react-native/typescript-config": "0.
|
|
89
|
+
"@react-native/babel-preset": "0.83.0-rc.4",
|
|
90
|
+
"@react-native/eslint-config": "0.83.0-rc.4",
|
|
91
|
+
"@react-native/metro-config": "0.83.0-rc.4",
|
|
92
|
+
"@react-native/typescript-config": "0.83.0-rc.4",
|
|
93
93
|
"@react-navigation/native": "^5.8.0",
|
|
94
94
|
"@react-navigation/stack": "^5.10.0",
|
|
95
95
|
"@types/jest": "^29.5.13",
|
|
96
|
-
"@types/react": "^19.
|
|
96
|
+
"@types/react": "^19.2.0",
|
|
97
97
|
"@types/react-test-renderer": "^19.1.0",
|
|
98
98
|
"@types/shelljs": "^0",
|
|
99
99
|
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
|
@@ -110,15 +110,15 @@
|
|
|
110
110
|
"jest": "^29.6.3",
|
|
111
111
|
"lint-staged": "^14.0.1",
|
|
112
112
|
"prettier": "^2.8.8",
|
|
113
|
-
"react": "^19.
|
|
113
|
+
"react": "^19.2.0",
|
|
114
114
|
"react-dom": "^19.1.0",
|
|
115
|
-
"react-native": "0.
|
|
115
|
+
"react-native": "0.83.0-rc.4",
|
|
116
116
|
"react-native-builder-bob": "^0.23.2",
|
|
117
117
|
"react-native-gesture-handler": "^2.28.0",
|
|
118
118
|
"react-native-reanimated": "^3.19.0",
|
|
119
119
|
"react-native-safe-area-context": "5.6.0",
|
|
120
120
|
"react-native-windows": "^0.64.8",
|
|
121
|
-
"react-test-renderer": "^19.
|
|
121
|
+
"react-test-renderer": "^19.2.0",
|
|
122
122
|
"release-it": "^15.6.0",
|
|
123
123
|
"shelljs": "^0.9.2",
|
|
124
124
|
"typescript": "^5.8.3"
|