react-native-screens 3.32.0 → 3.33.0

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.
Files changed (142) hide show
  1. package/README.md +17 -13
  2. package/RNScreens.podspec +10 -52
  3. package/android/CMakeLists.txt +48 -4
  4. package/android/build.gradle +9 -81
  5. package/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +25 -16
  6. package/android/src/fabric/java/com/swmansion/rnscreens/NativeProxy.kt +53 -0
  7. package/android/src/main/cpp/NativeProxy.cpp +51 -0
  8. package/android/src/main/cpp/NativeProxy.h +35 -0
  9. package/android/src/main/cpp/OnLoad.cpp +8 -0
  10. package/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt +5 -2
  11. package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +4 -1
  12. package/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt +2 -2
  13. package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +36 -17
  14. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +134 -38
  15. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +52 -30
  16. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainerViewManager.kt +17 -7
  17. package/android/src/main/java/com/swmansion/rnscreens/ScreenEventDispatcher.kt +10 -2
  18. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +56 -27
  19. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragmentWrapper.kt +8 -1
  20. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +50 -19
  21. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +60 -37
  22. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragmentWrapper.kt +4 -0
  23. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +85 -58
  24. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt +128 -37
  25. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubview.kt +19 -4
  26. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubviewManager.kt +16 -10
  27. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackViewManager.kt +28 -25
  28. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +173 -78
  29. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +59 -24
  30. package/android/src/main/java/com/swmansion/rnscreens/ScreensModule.kt +30 -8
  31. package/android/src/main/java/com/swmansion/rnscreens/ScreensShadowNode.kt +3 -1
  32. package/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +101 -50
  33. package/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +29 -22
  34. package/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +7 -2
  35. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderAttachedEvent.kt +4 -1
  36. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderBackButtonClickedEvent.kt +4 -1
  37. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderDetachedEvent.kt +4 -1
  38. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderHeightChangeEvent.kt +5 -5
  39. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenAppearEvent.kt +4 -1
  40. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenDisappearEvent.kt +4 -1
  41. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenDismissedEvent.kt +8 -4
  42. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenTransitionProgressEvent.kt +7 -6
  43. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillAppearEvent.kt +4 -1
  44. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillDisappearEvent.kt +4 -1
  45. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarBlurEvent.kt +4 -1
  46. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarChangeTextEvent.kt +4 -3
  47. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarCloseEvent.kt +4 -1
  48. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarFocusEvent.kt +4 -1
  49. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarOpenEvent.kt +4 -1
  50. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarSearchButtonPressEvent.kt +9 -4
  51. package/android/src/main/java/com/swmansion/rnscreens/events/StackFinishTransitioningEvent.kt +4 -1
  52. package/android/src/main/java/com/swmansion/rnscreens/utils/DeviceUtils.kt +1 -5
  53. package/android/src/main/java/com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper.kt +214 -0
  54. package/android/src/main/jni/CMakeLists.txt +5 -4
  55. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java +3 -0
  56. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerInterface.java +1 -0
  57. package/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +10 -5
  58. package/android/src/paper/java/com/swmansion/rnscreens/NativeProxy.kt +19 -0
  59. package/android/src/paper/java/com/swmansion/rnscreens/NativeScreensModuleSpec.java +4 -0
  60. package/common/cpp/react/renderer/components/rnscreens/FrameCorrectionModes.h +51 -0
  61. package/common/cpp/react/renderer/components/rnscreens/RNSModalScreenShadowNode.cpp +2 -1
  62. package/common/cpp/react/renderer/components/rnscreens/RNSModalScreenShadowNode.h +1 -1
  63. package/common/cpp/react/renderer/components/rnscreens/RNSScreenComponentDescriptor.h +140 -1
  64. package/common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.cpp +51 -1
  65. package/common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.h +23 -1
  66. package/common/cpp/react/renderer/components/rnscreens/RNSScreenState.cpp +20 -0
  67. package/common/cpp/react/renderer/components/rnscreens/RNSScreenState.h +23 -1
  68. package/common/cpp/react/renderer/components/rnscreens/utils/RectUtil.h +36 -0
  69. package/cpp/RNSScreenRemovalListener.cpp +25 -0
  70. package/cpp/RNSScreenRemovalListener.h +20 -0
  71. package/ios/RNSConvert.h +1 -0
  72. package/ios/RNSModalScreen.mm +22 -0
  73. package/ios/RNSModule.mm +1 -1
  74. package/ios/RNSScreen.h +2 -1
  75. package/ios/RNSScreen.mm +27 -19
  76. package/ios/RNSScreenStack.mm +24 -77
  77. package/ios/RNSScreenStackAnimator.mm +43 -6
  78. package/ios/RNSScreenStackHeaderConfig.mm +49 -11
  79. package/ios/RNSScreenStackHeaderSubview.mm +8 -0
  80. package/ios/utils/UIView+RNSUtility.h +23 -0
  81. package/ios/utils/UIView+RNSUtility.mm +55 -0
  82. package/lib/commonjs/components/ScreenStack.js +8 -1
  83. package/lib/commonjs/components/ScreenStack.js.map +1 -1
  84. package/lib/commonjs/fabric/ModalScreenNativeComponent.js.map +1 -1
  85. package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
  86. package/lib/commonjs/native-stack/views/NativeStackView.js +2 -0
  87. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  88. package/lib/module/components/ScreenStack.js +8 -1
  89. package/lib/module/components/ScreenStack.js.map +1 -1
  90. package/lib/module/fabric/ModalScreenNativeComponent.js.map +1 -1
  91. package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
  92. package/lib/module/native-stack/views/NativeStackView.js +2 -0
  93. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  94. package/lib/typescript/components/ScreenStack.d.ts.map +1 -1
  95. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts +1 -0
  96. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts.map +1 -1
  97. package/lib/typescript/fabric/ScreenNativeComponent.d.ts +1 -0
  98. package/lib/typescript/fabric/ScreenNativeComponent.d.ts.map +1 -1
  99. package/lib/typescript/native-stack/types.d.ts +10 -0
  100. package/lib/typescript/native-stack/types.d.ts.map +1 -1
  101. package/lib/typescript/native-stack/views/NativeStackView.d.ts.map +1 -1
  102. package/lib/typescript/types.d.ts +10 -0
  103. package/lib/typescript/types.d.ts.map +1 -1
  104. package/native-stack/README.md +110 -99
  105. package/package.json +6 -3
  106. package/react-native.config.js +17 -15
  107. package/src/TransitionProgressContext.tsx +1 -1
  108. package/src/components/Screen.tsx +4 -4
  109. package/src/components/ScreenStack.tsx +11 -1
  110. package/src/components/ScreenStackHeaderConfig.tsx +5 -5
  111. package/src/components/ScreenStackHeaderConfig.web.tsx +6 -6
  112. package/src/components/SearchBar.tsx +4 -4
  113. package/src/core.ts +1 -1
  114. package/src/fabric/ModalScreenNativeComponent.ts +1 -0
  115. package/src/fabric/ScreenNativeComponent.ts +1 -0
  116. package/src/fabric/ScreenNavigationContainerNativeComponent.ts +1 -1
  117. package/src/fabric/ScreenStackHeaderConfigNativeComponent.ts +1 -1
  118. package/src/fabric/ScreenStackHeaderSubviewNativeComponent.ts +1 -1
  119. package/src/fabric/SearchBarNativeComponent.ts +1 -1
  120. package/src/gesture-handler/ScreenGestureDetector.tsx +5 -5
  121. package/src/gesture-handler/constraints.ts +5 -5
  122. package/src/gesture-handler/fabricUtils.ts +1 -1
  123. package/src/native-stack/contexts/GHContext.tsx +1 -1
  124. package/src/native-stack/navigators/createNativeStackNavigator.tsx +3 -3
  125. package/src/native-stack/types.tsx +14 -4
  126. package/src/native-stack/utils/getDefaultHeaderHeight.tsx +1 -1
  127. package/src/native-stack/utils/getStatusBarHeight.tsx +1 -1
  128. package/src/native-stack/utils/useAnimatedHeaderHeight.tsx +1 -1
  129. package/src/native-stack/utils/useBackPressSubscription.tsx +1 -1
  130. package/src/native-stack/utils/useHeaderHeight.tsx +1 -1
  131. package/src/native-stack/views/FontProcessor.tsx +1 -1
  132. package/src/native-stack/views/HeaderConfig.tsx +1 -1
  133. package/src/native-stack/views/NativeStackView.tsx +11 -9
  134. package/src/reanimated/ReanimatedHeaderHeightContext.tsx +1 -1
  135. package/src/reanimated/ReanimatedNativeStackScreen.tsx +5 -5
  136. package/src/reanimated/ReanimatedScreen.tsx +2 -2
  137. package/src/reanimated/ReanimatedScreenProvider.tsx +1 -1
  138. package/src/reanimated/useReanimatedHeaderHeight.tsx +1 -1
  139. package/src/reanimated/useReanimatedTransitionProgress.tsx +1 -1
  140. package/src/types.tsx +15 -5
  141. package/src/useTransitionProgress.tsx +1 -1
  142. package/windows/README.md +4 -1
@@ -7,27 +7,42 @@ import com.facebook.react.module.annotations.ReactModuleList
7
7
  import com.facebook.react.module.model.ReactModuleInfo
8
8
  import com.facebook.react.module.model.ReactModuleInfoProvider
9
9
  import com.facebook.react.uimanager.ViewManager
10
+ import com.swmansion.rnscreens.utils.ScreenDummyLayoutHelper
10
11
 
11
12
  @ReactModuleList(
12
13
  nativeModules = [
13
- ScreensModule::class
14
- ]
14
+ ScreensModule::class,
15
+ ],
15
16
  )
16
17
  class RNScreensPackage : TurboReactPackage() {
17
- override fun createViewManagers(reactContext: ReactApplicationContext) =
18
- listOf<ViewManager<*, *>>(
18
+ // We just retain it here. This object helps us tackle jumping content when using native header.
19
+ // See: https://github.com/software-mansion/react-native-screens/pull/2169
20
+ private var screenDummyLayoutHelper: ScreenDummyLayoutHelper? = null
21
+
22
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
23
+ // This is the earliest we lay our hands on react context.
24
+ // Moreover this is called before FabricUIManger has finished initializing, not to mention
25
+ // installing its C++ bindings - so we are safe in terms of creating this helper
26
+ // before RN starts creating shadow nodes.
27
+ // See https://github.com/software-mansion/react-native-screens/pull/2169
28
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
29
+ screenDummyLayoutHelper = ScreenDummyLayoutHelper(reactContext)
30
+ }
31
+
32
+ return listOf<ViewManager<*, *>>(
19
33
  ScreenContainerViewManager(),
20
34
  ScreenViewManager(),
21
35
  ModalScreenViewManager(),
22
36
  ScreenStackViewManager(),
23
37
  ScreenStackHeaderConfigViewManager(),
24
38
  ScreenStackHeaderSubviewManager(),
25
- SearchBarManager()
39
+ SearchBarManager(),
26
40
  )
41
+ }
27
42
 
28
43
  override fun getModule(
29
44
  s: String,
30
- reactApplicationContext: ReactApplicationContext
45
+ reactApplicationContext: ReactApplicationContext,
31
46
  ): NativeModule? {
32
47
  when (s) {
33
48
  ScreensModule.NAME -> return ScreensModule(reactApplicationContext)
@@ -35,20 +50,24 @@ class RNScreensPackage : TurboReactPackage() {
35
50
  return null
36
51
  }
37
52
 
38
- override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
39
- return ReactModuleInfoProvider {
53
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider =
54
+ ReactModuleInfoProvider {
40
55
  val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
41
56
  val isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
42
- moduleInfos[ScreensModule.NAME] = ReactModuleInfo(
43
- ScreensModule.NAME,
44
- ScreensModule.NAME,
45
- false, // canOverrideExistingModule
46
- false, // needsEagerInit
47
- true, // hasConstants
48
- false, // isCxxModule
49
- isTurboModule
50
- )
57
+ moduleInfos[ScreensModule.NAME] =
58
+ ReactModuleInfo(
59
+ ScreensModule.NAME,
60
+ ScreensModule.NAME,
61
+ false, // canOverrideExistingModule
62
+ false, // needsEagerInit
63
+ true, // hasConstants
64
+ false, // isCxxModule
65
+ isTurboModule,
66
+ )
51
67
  moduleInfos
52
68
  }
69
+
70
+ companion object {
71
+ const val TAG = "RNScreensPackage"
53
72
  }
54
73
  }
@@ -6,6 +6,7 @@ import android.graphics.Paint
6
6
  import android.os.Parcelable
7
7
  import android.util.SparseArray
8
8
  import android.util.TypedValue
9
+ import android.view.View
9
10
  import android.view.ViewGroup
10
11
  import android.view.WindowManager
11
12
  import android.webkit.WebView
@@ -19,7 +20,9 @@ import com.facebook.react.uimanager.UIManagerModule
19
20
  import com.swmansion.rnscreens.events.HeaderHeightChangeEvent
20
21
 
21
22
  @SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated.
22
- class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
23
+ class Screen(
24
+ context: ReactContext?,
25
+ ) : FabricEnabledViewGroup(context) {
23
26
  val fragment: Fragment?
24
27
  get() = fragmentWrapper?.fragment
25
28
 
@@ -35,6 +38,7 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
35
38
  var screenOrientation: Int? = null
36
39
  private set
37
40
  var isStatusBarAnimated: Boolean? = null
41
+ var isBeingRemoved = false
38
42
 
39
43
  init {
40
44
  // we set layout params as WindowManager.LayoutParams to workaround the issue with TextInputs
@@ -60,13 +64,20 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
60
64
  // ignore restoring instance state too as we are not saving anything anyways.
61
65
  }
62
66
 
63
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
67
+ override fun onLayout(
68
+ changed: Boolean,
69
+ l: Int,
70
+ t: Int,
71
+ r: Int,
72
+ b: Int,
73
+ ) {
64
74
  if (container is ScreenStack && changed) {
65
75
  val width = r - l
66
76
  val height = b - t
67
77
 
68
78
  val headerHeight = calculateHeaderHeight()
69
- val totalHeight = headerHeight.first + headerHeight.second // action bar height + status bar height
79
+ val totalHeight =
80
+ headerHeight.first + headerHeight.second // action bar height + status bar height
70
81
  if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
71
82
  updateScreenSizeFabric(width, height, totalHeight)
72
83
  } else {
@@ -77,7 +88,10 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
77
88
  }
78
89
  }
79
90
 
80
- private fun updateScreenSizePaper(width: Int, height: Int) {
91
+ private fun updateScreenSizePaper(
92
+ width: Int,
93
+ height: Int,
94
+ ) {
81
95
  val reactContext = context as ReactContext
82
96
  reactContext.runOnNativeModulesQueueThread(
83
97
  object : GuardedRunnable(reactContext.exceptionHandler) {
@@ -86,7 +100,8 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
86
100
  .getNativeModule(UIManagerModule::class.java)
87
101
  ?.updateNodeSize(id, width, height)
88
102
  }
89
- })
103
+ },
104
+ )
90
105
  }
91
106
 
92
107
  val headerConfig: ScreenStackHeaderConfig?
@@ -108,7 +123,7 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
108
123
  }
109
124
  super.setLayerType(
110
125
  if (transitioning && !isWebViewInScreen) LAYER_TYPE_HARDWARE else LAYER_TYPE_NONE,
111
- null
126
+ null,
112
127
  )
113
128
  }
114
129
 
@@ -126,7 +141,10 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
126
141
  return false
127
142
  }
128
143
 
129
- override fun setLayerType(layerType: Int, paint: Paint?) {
144
+ override fun setLayerType(
145
+ layerType: Int,
146
+ paint: Paint?,
147
+ ) {
130
148
  // ignore - layer type is controlled by `transitioning` prop
131
149
  }
132
150
 
@@ -144,16 +162,17 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
144
162
  return
145
163
  }
146
164
  ScreenWindowTraits.applyDidSetOrientation()
147
- this.screenOrientation = when (screenOrientation) {
148
- "all" -> ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
149
- "portrait" -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
150
- "portrait_up" -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
151
- "portrait_down" -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
152
- "landscape" -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
153
- "landscape_left" -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
154
- "landscape_right" -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
155
- else -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
156
- }
165
+ this.screenOrientation =
166
+ when (screenOrientation) {
167
+ "all" -> ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
168
+ "portrait" -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
169
+ "portrait_up" -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
170
+ "portrait_down" -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
171
+ "landscape" -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
172
+ "landscape_left" -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
173
+ "landscape_right" -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
174
+ else -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
175
+ }
157
176
 
158
177
  fragmentWrapper?.let { ScreenWindowTraits.setOrientation(this, it.tryGetActivity()) }
159
178
  }
@@ -171,7 +190,13 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
171
190
  ScreenWindowTraits.applyDidSetStatusBarAppearance()
172
191
  }
173
192
  field = statusBarStyle
174
- fragmentWrapper?.let { ScreenWindowTraits.setStyle(this, it.tryGetActivity(), it.tryGetContext()) }
193
+ fragmentWrapper?.let {
194
+ ScreenWindowTraits.setStyle(
195
+ this,
196
+ it.tryGetActivity(),
197
+ it.tryGetContext(),
198
+ )
199
+ }
175
200
  }
176
201
 
177
202
  var isStatusBarHidden: Boolean? = null
@@ -193,7 +218,7 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
193
218
  ScreenWindowTraits.setTranslucent(
194
219
  this,
195
220
  it.tryGetActivity(),
196
- it.tryGetContext()
221
+ it.tryGetContext(),
197
222
  )
198
223
  }
199
224
  }
@@ -204,7 +229,13 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
204
229
  ScreenWindowTraits.applyDidSetStatusBarAppearance()
205
230
  }
206
231
  field = statusBarColor
207
- fragmentWrapper?.let { ScreenWindowTraits.setColor(this, it.tryGetActivity(), it.tryGetContext()) }
232
+ fragmentWrapper?.let {
233
+ ScreenWindowTraits.setColor(
234
+ this,
235
+ it.tryGetActivity(),
236
+ it.tryGetContext(),
237
+ )
238
+ }
208
239
  }
209
240
 
210
241
  var navigationBarColor: Int? = null
@@ -213,7 +244,12 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
213
244
  ScreenWindowTraits.applyDidSetNavigationBarAppearance()
214
245
  }
215
246
  field = navigationBarColor
216
- fragmentWrapper?.let { ScreenWindowTraits.setNavigationBarColor(this, it.tryGetActivity()) }
247
+ fragmentWrapper?.let {
248
+ ScreenWindowTraits.setNavigationBarColor(
249
+ this,
250
+ it.tryGetActivity(),
251
+ )
252
+ }
217
253
  }
218
254
 
219
255
  var isNavigationBarTranslucent: Boolean? = null
@@ -246,22 +282,61 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
246
282
 
247
283
  var nativeBackButtonDismissalEnabled: Boolean = true
248
284
 
285
+ fun startRemovalTransition() {
286
+ if (!isBeingRemoved) {
287
+ isBeingRemoved = true
288
+ startTransitionRecursive(this)
289
+ }
290
+ }
291
+
292
+ private fun startTransitionRecursive(parent: ViewGroup?) {
293
+ parent?.let {
294
+ for (i in 0 until it.childCount) {
295
+ val child = it.getChildAt(i)
296
+ if (child.javaClass.simpleName.equals("CircleImageView")) {
297
+ // SwipeRefreshLayout class which has CircleImageView as a child,
298
+ // does not handle `startViewTransition` properly.
299
+ // It has a custom `getChildDrawingOrder` method which returns
300
+ // wrong index if we called `startViewTransition` on the views on new arch.
301
+ // We add a simple View to bump the number of children to make it work.
302
+ // TODO: find a better way to handle this scenario
303
+ it.addView(View(context), i)
304
+ } else {
305
+ child?.let { view -> it.startViewTransition(view) }
306
+ }
307
+ if (child is ScreenStackHeaderConfig) {
308
+ // we want to start transition on children of the toolbar too,
309
+ // which is not a child of ScreenStackHeaderConfig
310
+ startTransitionRecursive(child.toolbar)
311
+ }
312
+ if (child is ViewGroup) {
313
+ startTransitionRecursive(child)
314
+ }
315
+ }
316
+ }
317
+ }
318
+
249
319
  private fun calculateHeaderHeight(): Pair<Double, Double> {
250
320
  val actionBarTv = TypedValue()
251
- val resolvedActionBarSize = context.theme.resolveAttribute(android.R.attr.actionBarSize, actionBarTv, true)
321
+ val resolvedActionBarSize =
322
+ context.theme.resolveAttribute(android.R.attr.actionBarSize, actionBarTv, true)
252
323
 
253
324
  // Check if it's possible to get an attribute from theme context and assign a value from it.
254
325
  // Otherwise, the default value will be returned.
255
- val actionBarHeight = TypedValue.complexToDimensionPixelSize(actionBarTv.data, resources.displayMetrics)
256
- .takeIf { resolvedActionBarSize && headerConfig?.isHeaderHidden != true && headerConfig?.isHeaderTranslucent != true }
257
- ?.let { PixelUtil.toDIPFromPixel(it.toFloat()).toDouble() } ?: 0.0
258
-
259
- val statusBarHeight = context.resources.getIdentifier("status_bar_height", "dimen", "android")
260
- // Count only status bar when action bar is visible and status bar is not hidden
261
- .takeIf { it > 0 && isStatusBarHidden != true && actionBarHeight > 0 }
262
- ?.let { (context.resources::getDimensionPixelSize)(it) }
263
- ?.let { PixelUtil.toDIPFromPixel(it.toFloat()).toDouble() }
264
- ?: 0.0
326
+ val actionBarHeight =
327
+ TypedValue
328
+ .complexToDimensionPixelSize(actionBarTv.data, resources.displayMetrics)
329
+ .takeIf { resolvedActionBarSize && headerConfig?.isHeaderHidden != true && headerConfig?.isHeaderTranslucent != true }
330
+ ?.let { PixelUtil.toDIPFromPixel(it.toFloat()).toDouble() } ?: 0.0
331
+
332
+ val statusBarHeight =
333
+ context.resources
334
+ .getIdentifier("status_bar_height", "dimen", "android")
335
+ // Count only status bar when action bar is visible and status bar is not hidden
336
+ .takeIf { it > 0 && isStatusBarHidden != true && actionBarHeight > 0 }
337
+ ?.let { (context.resources::getDimensionPixelSize)(it) }
338
+ ?.let { PixelUtil.toDIPFromPixel(it.toFloat()).toDouble() }
339
+ ?: 0.0
265
340
 
266
341
  return actionBarHeight to statusBarHeight
267
342
  }
@@ -269,27 +344,48 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) {
269
344
  private fun notifyHeaderHeightChange(headerHeight: Double) {
270
345
  val screenContext = context as ReactContext
271
346
  val surfaceId = UIManagerHelper.getSurfaceId(screenContext)
272
- UIManagerHelper.getEventDispatcherForReactTag(screenContext, id)
347
+ UIManagerHelper
348
+ .getEventDispatcherForReactTag(screenContext, id)
273
349
  ?.dispatchEvent(HeaderHeightChangeEvent(surfaceId, id, headerHeight))
274
350
  }
275
351
 
276
352
  enum class StackPresentation {
277
- PUSH, MODAL, TRANSPARENT_MODAL
353
+ PUSH,
354
+ MODAL,
355
+ TRANSPARENT_MODAL,
278
356
  }
279
357
 
280
358
  enum class StackAnimation {
281
- DEFAULT, NONE, FADE, SLIDE_FROM_BOTTOM, SLIDE_FROM_RIGHT, SLIDE_FROM_LEFT, FADE_FROM_BOTTOM, IOS
359
+ DEFAULT,
360
+ NONE,
361
+ FADE,
362
+ SLIDE_FROM_BOTTOM,
363
+ SLIDE_FROM_RIGHT,
364
+ SLIDE_FROM_LEFT,
365
+ FADE_FROM_BOTTOM,
366
+ IOS,
282
367
  }
283
368
 
284
369
  enum class ReplaceAnimation {
285
- PUSH, POP
370
+ PUSH,
371
+ POP,
286
372
  }
287
373
 
288
374
  enum class ActivityState {
289
- INACTIVE, TRANSITIONING_OR_BELOW_TOP, ON_TOP
375
+ INACTIVE,
376
+ TRANSITIONING_OR_BELOW_TOP,
377
+ ON_TOP,
290
378
  }
291
379
 
292
380
  enum class WindowTraits {
293
- ORIENTATION, COLOR, STYLE, TRANSLUCENT, HIDDEN, ANIMATED, NAVIGATION_BAR_COLOR, NAVIGATION_BAR_TRANSLUCENT, NAVIGATION_BAR_HIDDEN
381
+ ORIENTATION,
382
+ COLOR,
383
+ STYLE,
384
+ TRANSLUCENT,
385
+ HIDDEN,
386
+ ANIMATED,
387
+ NAVIGATION_BAR_COLOR,
388
+ NAVIGATION_BAR_TRANSLUCENT,
389
+ NAVIGATION_BAR_HIDDEN,
294
390
  }
295
391
  }
@@ -19,27 +19,37 @@ import com.facebook.react.uimanager.UIManagerHelper
19
19
  import com.swmansion.rnscreens.Screen.ActivityState
20
20
  import com.swmansion.rnscreens.events.ScreenDismissedEvent
21
21
 
22
- open class ScreenContainer(context: Context?) : ViewGroup(context) {
22
+ open class ScreenContainer(
23
+ context: Context?,
24
+ ) : ViewGroup(context) {
23
25
  @JvmField
24
26
  protected val screenWrappers = ArrayList<ScreenFragmentWrapper>()
27
+
25
28
  @JvmField
26
29
  protected var fragmentManager: FragmentManager? = null
27
30
  private var isAttached = false
28
31
  private var needsUpdate = false
29
32
  private var isLayoutEnqueued = false
30
- private val layoutCallback: ChoreographerCompat.FrameCallback = object : ChoreographerCompat.FrameCallback() {
31
- override fun doFrame(frameTimeNanos: Long) {
32
- isLayoutEnqueued = false
33
- measure(
34
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
35
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
36
- )
37
- layout(left, top, right, bottom)
33
+ private val layoutCallback: ChoreographerCompat.FrameCallback =
34
+ object : ChoreographerCompat.FrameCallback() {
35
+ override fun doFrame(frameTimeNanos: Long) {
36
+ isLayoutEnqueued = false
37
+ measure(
38
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
39
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
40
+ )
41
+ layout(left, top, right, bottom)
42
+ }
38
43
  }
39
- }
40
44
  private var parentScreenWrapper: ScreenFragmentWrapper? = null
41
45
 
42
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
46
+ override fun onLayout(
47
+ changed: Boolean,
48
+ l: Int,
49
+ t: Int,
50
+ r: Int,
51
+ b: Int,
52
+ ) {
43
53
  var i = 0
44
54
  val size = childCount
45
55
  while (i < size) {
@@ -69,9 +79,11 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) {
69
79
  isLayoutEnqueued = true
70
80
  // we use NATIVE_ANIMATED_MODULE choreographer queue because it allows us to catch the current
71
81
  // looper loop instead of enqueueing the update in the next loop causing a one frame delay.
72
- ReactChoreographer.getInstance()
82
+ ReactChoreographer
83
+ .getInstance()
73
84
  .postFrameCallback(
74
- ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE, layoutCallback
85
+ ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE,
86
+ layoutCallback,
75
87
  )
76
88
  }
77
89
  }
@@ -85,7 +97,10 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) {
85
97
 
86
98
  protected open fun adapt(screen: Screen): ScreenFragmentWrapper = ScreenFragment(screen)
87
99
 
88
- fun addScreen(screen: Screen, index: Int) {
100
+ fun addScreen(
101
+ screen: Screen,
102
+ index: Int,
103
+ ) {
89
104
  val fragment = adapt(screen)
90
105
  screen.fragmentWrapper = fragment
91
106
  screenWrappers.add(index, fragment)
@@ -173,7 +188,7 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) {
173
188
  parentScreenWrapper = fragmentWrapper
174
189
  fragmentWrapper.addChildScreenContainer(this)
175
190
  setFragmentManager(fragmentWrapper.fragment.childFragmentManager)
176
- }
191
+ },
177
192
  ) { "Parent Screen does not have its Fragment attached" }
178
193
  } else {
179
194
  // we expect top level view to be of type ReactRootView, this isn't really necessary but in
@@ -186,13 +201,15 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) {
186
201
  }
187
202
  }
188
203
 
189
- protected fun createTransaction(): FragmentTransaction {
190
- return requireNotNull(fragmentManager) { "fragment manager is null when creating transaction" }
204
+ protected fun createTransaction(): FragmentTransaction =
205
+ requireNotNull(fragmentManager) { "fragment manager is null when creating transaction" }
191
206
  .beginTransaction()
192
207
  .setReorderingAllowed(true)
193
- }
194
208
 
195
- private fun attachScreen(transaction: FragmentTransaction, fragment: Fragment) {
209
+ private fun attachScreen(
210
+ transaction: FragmentTransaction,
211
+ fragment: Fragment,
212
+ ) {
196
213
  transaction.add(id, fragment)
197
214
  }
198
215
 
@@ -228,15 +245,16 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) {
228
245
  }
229
246
  }
230
247
 
231
- private fun detachScreen(transaction: FragmentTransaction, fragment: Fragment) {
248
+ private fun detachScreen(
249
+ transaction: FragmentTransaction,
250
+ fragment: Fragment,
251
+ ) {
232
252
  transaction.remove(fragment)
233
253
  }
234
254
 
235
- private fun getActivityState(screenFragmentWrapper: ScreenFragmentWrapper): ActivityState? =
236
- screenFragmentWrapper.screen.activityState
255
+ private fun getActivityState(screenFragmentWrapper: ScreenFragmentWrapper): ActivityState? = screenFragmentWrapper.screen.activityState
237
256
 
238
- open fun hasScreen(screenFragmentWrapper: ScreenFragmentWrapper?): Boolean =
239
- screenWrappers.contains(screenFragmentWrapper)
257
+ open fun hasScreen(screenFragmentWrapper: ScreenFragmentWrapper?): Boolean = screenWrappers.contains(screenFragmentWrapper)
240
258
 
241
259
  override fun onAttachedToWindow() {
242
260
  super.onAttachedToWindow()
@@ -295,7 +313,10 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) {
295
313
  }
296
314
  }
297
315
 
298
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
316
+ override fun onMeasure(
317
+ widthMeasureSpec: Int,
318
+ heightMeasureSpec: Int,
319
+ ) {
299
320
  super.onMeasure(widthMeasureSpec, heightMeasureSpec)
300
321
  for (i in 0 until childCount) {
301
322
  getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec)
@@ -342,11 +363,12 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) {
342
363
  open fun onUpdate() {
343
364
  createTransaction().let {
344
365
  // detach screens that are no longer active
345
- val orphaned: MutableSet<Fragment> = HashSet(
346
- requireNotNull(fragmentManager) {
347
- "fragment manager is null when performing update in ScreenContainer"
348
- }.fragments
349
- )
366
+ val orphaned: MutableSet<Fragment> =
367
+ HashSet(
368
+ requireNotNull(fragmentManager) {
369
+ "fragment manager is null when performing update in ScreenContainer"
370
+ }.fragments,
371
+ )
350
372
  for (fragmentWrapper in screenWrappers) {
351
373
  if (getActivityState(fragmentWrapper) === ActivityState.INACTIVE &&
352
374
  fragmentWrapper.fragment.isAdded
@@ -11,27 +11,34 @@ import com.facebook.react.viewmanagers.RNSScreenContainerManagerDelegate
11
11
  import com.facebook.react.viewmanagers.RNSScreenContainerManagerInterface
12
12
 
13
13
  @ReactModule(name = ScreenContainerViewManager.REACT_CLASS)
14
- class ScreenContainerViewManager : ViewGroupManager<ScreenContainer>(), RNSScreenContainerManagerInterface<ScreenContainer> {
14
+ class ScreenContainerViewManager :
15
+ ViewGroupManager<ScreenContainer>(),
16
+ RNSScreenContainerManagerInterface<ScreenContainer> {
15
17
  private val delegate: ViewManagerDelegate<ScreenContainer>
16
18
 
17
19
  init {
18
20
  delegate = RNSScreenContainerManagerDelegate<ScreenContainer, ScreenContainerViewManager>(this)
19
21
  }
20
22
 
21
- protected override fun getDelegate(): ViewManagerDelegate<ScreenContainer> {
22
- return delegate
23
- }
23
+ protected override fun getDelegate(): ViewManagerDelegate<ScreenContainer> = delegate
24
24
 
25
25
  override fun getName(): String = REACT_CLASS
26
26
 
27
27
  override fun createViewInstance(reactContext: ThemedReactContext): ScreenContainer = ScreenContainer(reactContext)
28
28
 
29
- override fun addView(parent: ScreenContainer, child: View, index: Int) {
29
+ override fun addView(
30
+ parent: ScreenContainer,
31
+ child: View,
32
+ index: Int,
33
+ ) {
30
34
  require(child is Screen) { "Attempt attach child that is not of type RNScreens" }
31
35
  parent.addScreen(child, index)
32
36
  }
33
37
 
34
- override fun removeViewAt(parent: ScreenContainer, index: Int) {
38
+ override fun removeViewAt(
39
+ parent: ScreenContainer,
40
+ index: Int,
41
+ ) {
35
42
  parent.removeScreenAt(index)
36
43
  }
37
44
 
@@ -41,7 +48,10 @@ class ScreenContainerViewManager : ViewGroupManager<ScreenContainer>(), RNSScree
41
48
 
42
49
  override fun getChildCount(parent: ScreenContainer): Int = parent.screenCount
43
50
 
44
- override fun getChildAt(parent: ScreenContainer, index: Int): View = parent.getScreenAt(index)
51
+ override fun getChildAt(
52
+ parent: ScreenContainer,
53
+ index: Int,
54
+ ): View = parent.getScreenAt(index)
45
55
 
46
56
  override fun createShadowNodeInstance(context: ReactApplicationContext): LayoutShadowNode = ScreensShadowNode(context)
47
57
 
@@ -2,12 +2,16 @@ package com.swmansion.rnscreens
2
2
 
3
3
  interface ScreenEventDispatcher {
4
4
  fun canDispatchLifecycleEvent(event: ScreenFragment.ScreenLifecycleEvent): Boolean
5
+
5
6
  fun updateLastEventDispatched(event: ScreenFragment.ScreenLifecycleEvent)
6
7
 
7
8
  /**
8
9
  * Dispatches given screen lifecycle event to JS using screen from given fragment `fragmentWrapper`
9
10
  */
10
- fun dispatchLifecycleEvent(event: ScreenFragment.ScreenLifecycleEvent, fragmentWrapper: ScreenFragmentWrapper)
11
+ fun dispatchLifecycleEvent(
12
+ event: ScreenFragment.ScreenLifecycleEvent,
13
+ fragmentWrapper: ScreenFragmentWrapper,
14
+ )
11
15
 
12
16
  /**
13
17
  * Dispatches given screen lifecycle event from all non-empty child containers to JS
@@ -15,7 +19,11 @@ interface ScreenEventDispatcher {
15
19
  fun dispatchLifecycleEventInChildContainers(event: ScreenFragment.ScreenLifecycleEvent)
16
20
 
17
21
  fun dispatchHeaderBackButtonClickedEvent()
18
- fun dispatchTransitionProgressEvent(alpha: Float, closing: Boolean)
22
+
23
+ fun dispatchTransitionProgressEvent(
24
+ alpha: Float,
25
+ closing: Boolean,
26
+ )
19
27
 
20
28
  // Concrete dispatchers
21
29
  }