react-native-screens 3.9.0 → 3.11.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 (102) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +47 -7
  3. package/android/build.gradle +1 -2
  4. package/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt +71 -0
  5. package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +7 -0
  6. package/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt +29 -0
  7. package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +2 -1
  8. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +35 -52
  9. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +1 -1
  10. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +83 -34
  11. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +38 -33
  12. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +77 -42
  13. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +25 -9
  14. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt +8 -0
  15. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubview.kt +7 -1
  16. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubviewManager.kt +1 -0
  17. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +10 -0
  18. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +72 -11
  19. package/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +107 -0
  20. package/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +155 -0
  21. package/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +67 -0
  22. package/android/src/main/res/anim/rns_default_enter_in.xml +18 -0
  23. package/android/src/main/res/anim/rns_default_enter_out.xml +19 -0
  24. package/android/src/main/res/anim/rns_default_exit_in.xml +17 -0
  25. package/android/src/main/res/anim/rns_default_exit_out.xml +18 -0
  26. package/android/src/main/res/anim/rns_fade_in.xml +7 -0
  27. package/android/src/main/res/anim/rns_fade_out.xml +7 -0
  28. package/android/src/main/res/anim/rns_no_animation_20.xml +6 -0
  29. package/createNativeStackNavigator/README.md +12 -0
  30. package/ios/RNSScreen.h +10 -0
  31. package/ios/RNSScreen.m +38 -0
  32. package/ios/RNSScreenContainer.m +5 -0
  33. package/ios/RNSScreenStack.m +29 -13
  34. package/ios/RNSScreenStackAnimator.m +45 -14
  35. package/ios/RNSScreenStackHeaderConfig.m +4 -1
  36. package/ios/RNSScreenWindowTraits.h +1 -0
  37. package/ios/RNSScreenWindowTraits.m +20 -0
  38. package/ios/UIViewController+RNScreens.m +10 -0
  39. package/lib/commonjs/index.js +17 -1
  40. package/lib/commonjs/index.js.map +1 -1
  41. package/lib/commonjs/index.native.js +66 -18
  42. package/lib/commonjs/index.native.js.map +1 -1
  43. package/lib/commonjs/native-stack/utils/useBackPressSubscription.js +67 -0
  44. package/lib/commonjs/native-stack/utils/useBackPressSubscription.js.map +1 -0
  45. package/lib/commonjs/native-stack/views/HeaderConfig.js +46 -4
  46. package/lib/commonjs/native-stack/views/HeaderConfig.js.map +1 -1
  47. package/lib/commonjs/native-stack/views/NativeStackView.js +33 -4
  48. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  49. package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js +60 -0
  50. package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js.map +1 -0
  51. package/lib/commonjs/reanimated/ReanimatedScreen.js +7 -79
  52. package/lib/commonjs/reanimated/ReanimatedScreen.js.map +1 -1
  53. package/lib/commonjs/reanimated/ReanimatedScreenProvider.js +61 -0
  54. package/lib/commonjs/reanimated/ReanimatedScreenProvider.js.map +1 -0
  55. package/lib/commonjs/reanimated/index.js +2 -2
  56. package/lib/commonjs/reanimated/index.js.map +1 -1
  57. package/lib/commonjs/utils.js +20 -0
  58. package/lib/commonjs/utils.js.map +1 -0
  59. package/lib/module/index.js +1 -0
  60. package/lib/module/index.js.map +1 -1
  61. package/lib/module/index.native.js +65 -19
  62. package/lib/module/index.native.js.map +1 -1
  63. package/lib/module/native-stack/utils/useBackPressSubscription.js +50 -0
  64. package/lib/module/native-stack/utils/useBackPressSubscription.js.map +1 -0
  65. package/lib/module/native-stack/views/HeaderConfig.js +46 -5
  66. package/lib/module/native-stack/views/HeaderConfig.js.map +1 -1
  67. package/lib/module/native-stack/views/NativeStackView.js +33 -4
  68. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  69. package/lib/module/reanimated/ReanimatedNativeStackScreen.js +40 -0
  70. package/lib/module/reanimated/ReanimatedNativeStackScreen.js.map +1 -0
  71. package/lib/module/reanimated/ReanimatedScreen.js +6 -73
  72. package/lib/module/reanimated/ReanimatedScreen.js.map +1 -1
  73. package/lib/module/reanimated/ReanimatedScreenProvider.js +49 -0
  74. package/lib/module/reanimated/ReanimatedScreenProvider.js.map +1 -0
  75. package/lib/module/reanimated/index.js +1 -1
  76. package/lib/module/reanimated/index.js.map +1 -1
  77. package/lib/module/utils.js +8 -0
  78. package/lib/module/utils.js.map +1 -0
  79. package/lib/typescript/index.d.ts +1 -0
  80. package/lib/typescript/native-stack/types.d.ts +34 -2
  81. package/lib/typescript/native-stack/utils/useBackPressSubscription.d.ts +16 -0
  82. package/lib/typescript/reanimated/ReanimatedNativeStackScreen.d.ts +5 -0
  83. package/lib/typescript/reanimated/ReanimatedScreen.d.ts +5 -2
  84. package/lib/typescript/reanimated/ReanimatedScreenProvider.d.ts +2 -0
  85. package/lib/typescript/reanimated/index.d.ts +1 -1
  86. package/lib/typescript/types.d.ts +101 -1
  87. package/lib/typescript/utils.d.ts +2 -0
  88. package/native-stack/README.md +70 -8
  89. package/package.json +2 -1
  90. package/reanimated/package.json +6 -0
  91. package/src/index.native.tsx +94 -36
  92. package/src/index.tsx +4 -0
  93. package/src/native-stack/types.tsx +34 -2
  94. package/src/native-stack/utils/useBackPressSubscription.tsx +66 -0
  95. package/src/native-stack/views/HeaderConfig.tsx +46 -3
  96. package/src/native-stack/views/NativeStackView.tsx +33 -4
  97. package/src/reanimated/ReanimatedNativeStackScreen.tsx +61 -0
  98. package/src/reanimated/ReanimatedScreen.tsx +6 -84
  99. package/src/reanimated/ReanimatedScreenProvider.tsx +42 -0
  100. package/src/reanimated/index.tsx +1 -1
  101. package/src/types.tsx +101 -1
  102. package/src/utils.ts +12 -0
@@ -1,9 +1,9 @@
1
- /// <reference types="react" />
2
1
  import { Animated, NativeSyntheticEvent, ViewProps, View, TargetedEvent, TextInputFocusEventData } from 'react-native';
3
2
  export declare type StackPresentationTypes = 'push' | 'modal' | 'transparentModal' | 'containedModal' | 'containedTransparentModal' | 'fullScreenModal' | 'formSheet';
4
3
  export declare type StackAnimationTypes = 'default' | 'fade' | 'fade_from_bottom' | 'flip' | 'none' | 'simple_push' | 'slide_from_bottom' | 'slide_from_right' | 'slide_from_left';
5
4
  export declare type BlurEffectTypes = 'extraLight' | 'light' | 'dark' | 'regular' | 'prominent' | 'systemUltraThinMaterial' | 'systemThinMaterial' | 'systemMaterial' | 'systemThickMaterial' | 'systemChromeMaterial' | 'systemUltraThinMaterialLight' | 'systemThinMaterialLight' | 'systemMaterialLight' | 'systemThickMaterialLight' | 'systemChromeMaterialLight' | 'systemUltraThinMaterialDark' | 'systemThinMaterialDark' | 'systemMaterialDark' | 'systemThickMaterialDark' | 'systemChromeMaterialDark';
6
5
  export declare type ScreenReplaceTypes = 'push' | 'pop';
6
+ export declare type SwipeDirectionTypes = 'vertical' | 'horizontal';
7
7
  export declare type ScreenOrientationTypes = 'default' | 'all' | 'portrait' | 'portrait_up' | 'portrait_down' | 'landscape' | 'landscape_left' | 'landscape_right';
8
8
  export declare type HeaderSubviewTypes = 'back' | 'right' | 'left' | 'center' | 'searchBar';
9
9
  export declare type TransitionProgressEventType = {
@@ -43,6 +43,12 @@ export interface ScreenProps extends ViewProps {
43
43
  * @platform ios
44
44
  */
45
45
  gestureEnabled?: boolean;
46
+ /**
47
+ * Whether the home indicator should be hidden on this screen. Defaults to `false`.
48
+ *
49
+ * @platform ios
50
+ */
51
+ homeIndicatorHidden?: boolean;
46
52
  /**
47
53
  * Boolean indicating whether, when the Android default back button is clicked, the `pop` action should be performed on the native side or on the JS side to be able to prevent it.
48
54
  * Unfortunately the same behavior is not available on iOS since the behavior of native back button cannot be changed there.
@@ -51,6 +57,18 @@ export interface ScreenProps extends ViewProps {
51
57
  * @platform android
52
58
  */
53
59
  nativeBackButtonDismissalEnabled?: boolean;
60
+ /**
61
+ * Sets the navigation bar color. Defaults to initial status bar color.
62
+ *
63
+ * @platform android
64
+ */
65
+ navigationBarColor?: string;
66
+ /**
67
+ * Sets the visibility of the navigation bar. Defaults to `false`.
68
+ *
69
+ * @platform android
70
+ */
71
+ navigationBarHidden?: boolean;
54
72
  /**
55
73
  * A callback that gets called when the current screen appears.
56
74
  */
@@ -156,6 +174,23 @@ export interface ScreenProps extends ViewProps {
156
174
  * @platform android
157
175
  */
158
176
  statusBarTranslucent?: boolean;
177
+ /**
178
+ * Sets the direction in which you should swipe to dismiss the screen.
179
+ * When using `vertical` option, options `fullScreenSwipeEnabled: true`, `customAnimationOnSwipe: true` and `stackAnimation: 'slide_from_bottom'` are set by default.
180
+ * The following values are supported:
181
+ * - `vertical` – dismiss screen vertically
182
+ * - `horizontal` – dismiss screen horizontally (default)
183
+ *
184
+ * @platform ios
185
+ */
186
+ swipeDirection?: SwipeDirectionTypes;
187
+ /**
188
+ * Changes the duration (in milliseconds) of `slide_from_bottom`, `fade_from_bottom`, `fade` and `simple_push` transitions on iOS. Defaults to `350`.
189
+ * The duration of `default` and `flip` transitions isn't customizable.
190
+ *
191
+ * @platform ios
192
+ */
193
+ transitionDuration?: number;
159
194
  }
160
195
  export interface ScreenContainerProps extends ViewProps {
161
196
  children?: React.ReactNode;
@@ -270,6 +305,14 @@ export interface ScreenStackHeaderConfigProps extends ViewProps {
270
305
  * Boolean that allows for disabling drop shadow under navigation header when the edge of any scrollable content reaches the matching edge of the navigation bar.
271
306
  */
272
307
  largeTitleHideShadow?: boolean;
308
+ /**
309
+ * Callback which is executed when screen header is attached
310
+ */
311
+ onAttached?: () => void;
312
+ /**
313
+ * Callback which is executed when screen header is detached
314
+ */
315
+ onDetached?: () => void;
273
316
  /**
274
317
  * String that can be displayed in the header as a fallback for `headerTitle`.
275
318
  */
@@ -309,22 +352,46 @@ export interface SearchBarProps {
309
352
  * The auto-capitalization behavior
310
353
  */
311
354
  autoCapitalize?: 'none' | 'words' | 'sentences' | 'characters';
355
+ /**
356
+ * Automatically focuses search bar on mount
357
+ *
358
+ * @platform android
359
+ */
360
+ autoFocus?: boolean;
312
361
  /**
313
362
  * The search field background color
314
363
  */
315
364
  barTintColor?: string;
316
365
  /**
317
366
  * The text to be used instead of default `Cancel` button text
367
+ *
368
+ * @platform ios
318
369
  */
319
370
  cancelButtonText?: string;
371
+ /**
372
+ * Specifies whether the back button should close search bar's text input or not.
373
+ *
374
+ * @platform android
375
+ */
376
+ disableBackButtonOverride?: boolean;
320
377
  /**
321
378
  * Indicates whether to hide the navigation bar
379
+ *
380
+ * @platform ios
322
381
  */
323
382
  hideNavigationBar?: boolean;
324
383
  /**
325
384
  * Indicates whether to hide the search bar when scrolling
385
+ *
386
+ * @platform ios
326
387
  */
327
388
  hideWhenScrolling?: boolean;
389
+ /**
390
+ * Sets type of the input. Defaults to `text`.
391
+ *
392
+ * @platform android
393
+ */
394
+ inputType?: 'text' | 'phone' | 'number' | 'email';
328
395
  /**
329
396
  * Indicates whether to to obscure the underlying content
330
397
  */
@@ -335,16 +402,30 @@ export interface SearchBarProps {
335
402
  onBlur?: (e: NativeSyntheticEvent<TargetedEvent>) => void;
336
403
  /**
337
404
  * A callback that gets called when the cancel button is pressed
405
+ *
406
+ * @platform ios
338
407
  */
339
408
  onCancelButtonPress?: (e: NativeSyntheticEvent<TargetedEvent>) => void;
340
409
  /**
341
410
  * A callback that gets called when the text changes. It receives the current text value of the search bar.
342
411
  */
343
412
  onChangeText?: (e: NativeSyntheticEvent<TextInputFocusEventData>) => void;
413
+ /**
414
+ * A callback that gets called when search bar is closed
415
+ *
416
+ * @platform android
417
+ */
418
+ onClose?: () => void;
344
419
  /**
345
420
  * A callback that gets called when search bar has received focus
346
421
  */
347
422
  onFocus?: (e: NativeSyntheticEvent<TargetedEvent>) => void;
423
+ /**
424
+ * A callback that gets called when search bar is opened
425
+ *
426
+ * @platform android
427
+ */
428
+ onOpen?: () => void;
348
429
  /**
349
430
  * A callback that gets called when the search button is pressed. It receives the current text value of the search bar.
350
431
  */
@@ -357,4 +438,23 @@ export interface SearchBarProps {
357
438
  * The search field text color
358
439
  */
359
440
  textColor?: string;
441
+ /**
442
+ * The search hint text color
443
+ *
444
+ * @plaform android
445
+ */
446
+ hintTextColor?: string;
447
+ /**
448
+ * The search and close icon color shown in the header
449
+ *
450
+ * @plaform android
451
+ */
452
+ headerIconColor?: string;
453
+ /**
454
+ * Show the search hint icon when search bar is focused
455
+ *
456
+ * @plaform android
457
+ * @default true
458
+ */
459
+ shouldShowHintSearchIcon?: boolean;
360
460
  }
@@ -0,0 +1,2 @@
1
+ export declare const isSearchBarAvailableForCurrentPlatform: boolean;
2
+ export declare function executeNativeBackPress(): boolean;
@@ -182,6 +182,10 @@ A Boolean to that lets you opt out of insetting the header. You may want to * se
182
182
 
183
183
  Boolean indicating whether the navigation bar is translucent.
184
184
 
185
+ #### `homeIndicatorHidden` (iOS only)
186
+
187
+ Whether the home indicator should be hidden on this screen. Defaults to `false`.
188
+
185
189
  #### `nativeBackButtonDismissalEnabled` (Android only)
186
190
 
187
191
  Boolean indicating whether, when the Android default back button is clicked, the `pop` action should be performed on the native side or on the JS side to be able to prevent it.
@@ -189,6 +193,14 @@ Unfortunately the same behavior is not available on iOS since the behavior of na
189
193
 
190
194
  Defaults to `false`.
191
195
 
196
+ #### `navigationBarColor` (Android only)
197
+
198
+ Sets the navigation bar color. Defaults to initial status bar color.
199
+
200
+ #### `navigationBarHidden` (Android only)
201
+
202
+ Sets the visibility of the navigation bar. Defaults to `false`.
203
+
192
204
  #### `replaceAnimation`
193
205
 
194
206
  How should the screen replacing another screen animate.
@@ -230,10 +242,24 @@ Defaults to `push`.
230
242
 
231
243
  Using `containedModal` and `containedTransparentModal` with other types of modals in one native stack navigator is not recommended and can result in a freeze or a crash of the application.
232
244
 
245
+ #### `swipeDirection` (iOS only)
246
+
247
+ Sets the direction in which you should swipe to dismiss the screen. The following values are supported:
248
+ - `vertical` – dismiss screen vertically
249
+ - `horizontal` – dismiss screen horizontally (default)
250
+
251
+ When using `vertical` option, options `fullScreenSwipeEnabled: true`, `customAnimationOnSwipe: true` and `stackAnimation: 'slide_from_bottom'` are set by default.
252
+
233
253
  #### `title`
234
254
 
235
255
  A string that can be used as a fallback for `headerTitle`.
236
256
 
257
+ #### `transitionDuration` (iOS only)
258
+
259
+ Changes the duration (in milliseconds) of `slide_from_bottom`, `fade_from_bottom`, `fade` and `simple_push` transitions on iOS. Defaults to `350`.
260
+
261
+ The duration of `default` and `flip` transitions isn't customizable.
262
+
237
263
  #### `useTransitionProgress`
238
264
 
239
265
  Hook providing context value of transition progress of the current screen to be used with `react-native` `Animated`. It consists of 2 values:
@@ -353,12 +379,10 @@ Defaults to `auto`.
353
379
 
354
380
  Sets the translucency of the status bar (similar to the `StatusBar` component). Defaults to `false`.
355
381
 
356
- ### Search bar (iOS only)
382
+ ### Search bar
357
383
 
358
384
  The search bar is just a `searchBar` property that can be specified in the navigator's `screenOptions` or an individual screen's `options`. Search bars are rarely static so normally it is controlled by passing an object to `searchBar` navigation option in the component's body.
359
385
 
360
- Search bar is only supported on iOS.
361
-
362
386
  Example:
363
387
 
364
388
  ```js
@@ -385,7 +409,11 @@ Possible values:
385
409
  - `sentences`
386
410
  - `characters`
387
411
 
388
- Defaults to `sentences`.
412
+ Defaults to `sentences` on iOS and `'none'` on Android.
413
+
414
+ #### `autoFocus` (Android only)
415
+
416
+ When set to `true` focuses search bar automatically when screen is appearing. Default value is `false`.
389
417
 
390
418
  #### `barTintColor`
391
419
 
@@ -393,23 +421,37 @@ The search field background color.
393
421
 
394
422
  By default bar tint color is translucent.
395
423
 
396
- #### `cancelButtonText`
424
+ #### `cancelButtonText` (iOS only)
397
425
 
398
426
  The text to be used instead of default `Cancel` button text.
399
427
 
400
- #### `hideNavigationBar`
428
+ #### `disableBackButtonOverride` (Android only)
429
+
430
+ Default behavior is to prevent screen from going back when search bar is open (`disableBackButtonOverride: false`). If you don't want this to happen set `disableBackButtonOverride` to `true`
431
+
432
+ #### `hideNavigationBar` (iOS only)
401
433
 
402
434
  Boolean indicating whether to hide the navigation bar during searching.
403
435
 
404
436
  Defaults to `true`.
405
437
 
406
- #### `hideWhenScrolling`
438
+ #### `hideWhenScrolling` (iOS only)
407
439
 
408
440
  Boolean indicating whether to hide the search bar when scrolling.
409
441
 
410
442
  Defaults to `true`.
411
443
 
412
- #### `obscureBackground`
444
+ #### `inputType` (Android only)
445
+
446
+ This prop is used to change type of the input and keyboard. Default value is `'text'`.
447
+
448
+ All values:
449
+ - `'text'` - normal text input
450
+ - `'number'` - number input
451
+ - `'email'` - email input
452
+ - `'phone'` - phone input
453
+
454
+ #### `obscureBackground` (iOS only)
413
455
 
414
456
  Boolean indicating whether to obscure the underlying content with semi-transparent overlay.
415
457
 
@@ -440,11 +482,19 @@ React.useLayoutEffect(() => {
440
482
  });
441
483
  }, [navigation]);
442
484
  ```
485
+ #### `onClose` (Android only)
486
+
487
+ A callback that gets called when search bar is closing
488
+
443
489
 
444
490
  #### `onFocus`
445
491
 
446
492
  A callback that gets called when search bar has received focus.
447
493
 
494
+ #### `onOpen` (Android only)
495
+
496
+ A callback that gets called when search bar is expanding
497
+
448
498
  #### `onSearchButtonPress`
449
499
 
450
500
  A callback that gets called when the search button is pressed. It receives the current text value of the search bar.
@@ -459,6 +509,18 @@ Defaults to an empty string.
459
509
 
460
510
  The search field text color.
461
511
 
512
+ #### `hintTextColor`
513
+
514
+ The search hint text color. (Android only)
515
+
516
+ #### `headerIconColor`
517
+
518
+ The search and close icon color shown in the header. (Android only)
519
+
520
+ #### `shouldShowHintSearchIcon`
521
+
522
+ Show the search hint icon when search bar is focused. (Android only)
523
+
462
524
  ### Events
463
525
 
464
526
  The navigator can [emit events](https://reactnavigation.org/docs/navigation-events) on certain actions. Supported events are:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-screens",
3
- "version": "3.9.0",
3
+ "version": "3.11.0",
4
4
  "description": "Native navigation primitives for your React Native app.",
5
5
  "scripts": {
6
6
  "check-types": "tsc --noEmit",
@@ -82,6 +82,7 @@
82
82
  "eslint-plugin-node": "^11.1.0",
83
83
  "eslint-plugin-promise": "^4.2.1",
84
84
  "eslint-plugin-react": "^7.20.5",
85
+ "eslint-plugin-react-hooks": "^4.2.0",
85
86
  "eslint-plugin-react-native": "^3.2.1",
86
87
  "eslint-plugin-standard": "^4.0.1",
87
88
  "husky": "^7.0.1",
@@ -0,0 +1,6 @@
1
+ {
2
+ "main": "../lib/commonjs/reanimated/index",
3
+ "module": "../lib/module/reanimated/index",
4
+ "react-native": "../src/reanimated/index",
5
+ "types": "../lib/typescript/reanimated/index"
6
+ }
@@ -14,6 +14,7 @@ import { Freeze } from 'react-freeze';
14
14
  // @ts-ignore Getting private component
15
15
  // eslint-disable-next-line import/default
16
16
  import processColor from 'react-native/Libraries/StyleSheet/processColor';
17
+ import { version } from 'react-native/package.json';
17
18
 
18
19
  import TransitionProgressContext from './TransitionProgressContext';
19
20
  import useTransitionProgress from './useTransitionProgress';
@@ -30,6 +31,10 @@ import {
30
31
  ScreenStackHeaderConfigProps,
31
32
  SearchBarProps,
32
33
  } from './types';
34
+ import {
35
+ isSearchBarAvailableForCurrentPlatform,
36
+ executeNativeBackPress,
37
+ } from './utils';
33
38
 
34
39
  // web implementation is taken from `index.tsx`
35
40
  const isPlatformSupported =
@@ -51,6 +56,15 @@ function enableScreens(shouldEnableScreens = true): void {
51
56
  let ENABLE_FREEZE = false;
52
57
 
53
58
  function enableFreeze(shouldEnableReactFreeze = true): void {
59
+ const minor = parseInt(version.split('.')[1]); // eg. takes 66 from '0.66.0'
60
+
61
+ // react-freeze requires react-native >=0.64, react-native from main is 0.0.0
62
+ if (!(minor === 0 || minor >= 64) && shouldEnableReactFreeze) {
63
+ console.warn(
64
+ 'react-freeze library requires at least react-native 0.64. Please upgrade your react-native version in order to use this feature.'
65
+ );
66
+ }
67
+
54
68
  ENABLE_FREEZE = shouldEnableReactFreeze;
55
69
  }
56
70
 
@@ -130,15 +144,31 @@ const ScreensNativeModules = {
130
144
  },
131
145
  };
132
146
 
133
- function MaybeFreeze({
134
- freeze,
135
- children,
136
- }: {
147
+ interface FreezeWrapperProps {
137
148
  freeze: boolean;
138
149
  children: React.ReactNode;
139
- }) {
150
+ }
151
+
152
+ // This component allows one more render before freezing the screen.
153
+ // Allows activityState to reach the native side and useIsFocused to work correctly.
154
+ function DelayedFreeze({ freeze, children }: FreezeWrapperProps) {
155
+ // flag used for determining whether freeze should be enabled
156
+ const [freezeState, setFreezeState] = React.useState(false);
157
+
158
+ if (freeze !== freezeState) {
159
+ // setImmediate is executed at the end of the JS execution block.
160
+ // Used here for changing the state right after the render.
161
+ setImmediate(() => {
162
+ setFreezeState(freeze);
163
+ });
164
+ }
165
+
166
+ return <Freeze freeze={freeze ? freezeState : false}>{children}</Freeze>;
167
+ }
168
+
169
+ function MaybeFreeze({ freeze, children }: FreezeWrapperProps) {
140
170
  if (ENABLE_FREEZE) {
141
- return <Freeze freeze={freeze}>{children}</Freeze>;
171
+ return <DelayedFreeze freeze={freeze}>{children}</DelayedFreeze>;
142
172
  } else {
143
173
  return <>{children}</>;
144
174
  }
@@ -147,19 +177,32 @@ function MaybeFreeze({
147
177
  function ScreenStack(props: ScreenStackProps) {
148
178
  if (ENABLE_FREEZE) {
149
179
  const { children, ...rest } = props;
150
- const count = React.Children.count(children);
151
- const childrenWithProps = React.Children.map(children, (child, index) => {
152
- return <Freeze freeze={count - index > 2}>{child}</Freeze>;
153
- });
180
+ const size = React.Children.count(children);
181
+ // freezes all screens except the top one
182
+ const childrenWithFreeze = React.Children.map(children, (child, index) => (
183
+ <DelayedFreeze freeze={size - index > 1}>{child}</DelayedFreeze>
184
+ ));
154
185
  return (
155
186
  <ScreensNativeModules.NativeScreenStack {...rest}>
156
- {childrenWithProps}
187
+ {childrenWithFreeze}
157
188
  </ScreensNativeModules.NativeScreenStack>
158
189
  );
159
190
  }
160
191
  return <ScreensNativeModules.NativeScreenStack {...props} />;
161
192
  }
162
193
 
194
+ // Incomplete type, all accessible properties available at:
195
+ // react-native/Libraries/Components/View/ReactNativeViewViewConfig.js
196
+ interface ViewConfig extends View {
197
+ viewConfig: {
198
+ validAttributes: {
199
+ style: {
200
+ display: boolean;
201
+ };
202
+ };
203
+ };
204
+ }
205
+
163
206
  class Screen extends React.Component<ScreenProps> {
164
207
  private ref: React.ElementRef<typeof View> | null = null;
165
208
  private closing = new Animated.Value(0);
@@ -205,28 +248,38 @@ class Screen extends React.Component<ScreenProps> {
205
248
  const processedColor = processColor(statusBarColor);
206
249
 
207
250
  return (
208
- <AnimatedNativeScreen
209
- {...props}
210
- statusBarColor={processedColor}
211
- activityState={activityState}
212
- ref={this.setRef}
213
- onTransitionProgress={
214
- !isNativeStack
215
- ? undefined
216
- : Animated.event(
217
- [
218
- {
219
- nativeEvent: {
220
- progress: this.progress,
221
- closing: this.closing,
222
- goingForward: this.goingForward,
251
+ <MaybeFreeze freeze={activityState === 0}>
252
+ <AnimatedNativeScreen
253
+ {...props}
254
+ statusBarColor={processedColor}
255
+ activityState={activityState}
256
+ // This prevents showing blank screen when navigating between multiple screens with freezing
257
+ // https://github.com/software-mansion/react-native-screens/pull/1208
258
+ ref={(ref: ViewConfig) => {
259
+ if (ref?.viewConfig?.validAttributes?.style) {
260
+ ref.viewConfig.validAttributes.style = {
261
+ ...ref.viewConfig.validAttributes.style,
262
+ display: false,
263
+ };
264
+ }
265
+ this.setRef(ref);
266
+ }}
267
+ onTransitionProgress={
268
+ !isNativeStack
269
+ ? undefined
270
+ : Animated.event(
271
+ [
272
+ {
273
+ nativeEvent: {
274
+ progress: this.progress,
275
+ closing: this.closing,
276
+ goingForward: this.goingForward,
277
+ },
223
278
  },
224
- },
225
- ],
226
- { useNativeDriver: true }
227
- )
228
- }>
229
- <MaybeFreeze freeze={activityState === 0}>
279
+ ],
280
+ { useNativeDriver: true }
281
+ )
282
+ }>
230
283
  {!isNativeStack ? ( // see comment of this prop in types.tsx for information why it is needed
231
284
  children
232
285
  ) : (
@@ -239,8 +292,8 @@ class Screen extends React.Component<ScreenProps> {
239
292
  {children}
240
293
  </TransitionProgressContext.Provider>
241
294
  )}
242
- </MaybeFreeze>
243
- </AnimatedNativeScreen>
295
+ </AnimatedNativeScreen>
296
+ </MaybeFreeze>
244
297
  );
245
298
  } else {
246
299
  // same reason as above
@@ -383,8 +436,10 @@ module.exports = {
383
436
  return ScreensNativeModules.NativeScreenStackHeaderSubview;
384
437
  },
385
438
  get SearchBar() {
386
- if (Platform.OS !== 'ios') {
387
- console.warn('Importing SearchBar is only valid on iOS devices.');
439
+ if (!isSearchBarAvailableForCurrentPlatform) {
440
+ console.warn(
441
+ 'Importing SearchBar is only valid on iOS and Android devices.'
442
+ );
388
443
  return View;
389
444
  }
390
445
 
@@ -411,4 +466,7 @@ module.exports = {
411
466
  screensEnabled,
412
467
  shouldUseActivityState,
413
468
  useTransitionProgress,
469
+
470
+ isSearchBarAvailableForCurrentPlatform,
471
+ executeNativeBackPress,
414
472
  };
package/src/index.tsx CHANGED
@@ -11,6 +11,10 @@ import {
11
11
 
12
12
  export * from './types';
13
13
  export { default as useTransitionProgress } from './useTransitionProgress';
14
+ export {
15
+ isSearchBarAvailableForCurrentPlatform,
16
+ executeNativeBackPress,
17
+ } from './utils';
14
18
 
15
19
  let ENABLE_SCREENS = true;
16
20
 
@@ -243,6 +243,12 @@ export type NativeStackNavigationOptions = {
243
243
  * Boolean indicating whether the navigation bar is translucent.
244
244
  */
245
245
  headerTranslucent?: boolean;
246
+ /**
247
+ * Whether the home indicator should be hidden on this screen. Defaults to `false`.
248
+ *
249
+ * @platform ios
250
+ */
251
+ homeIndicatorHidden?: boolean;
246
252
  /**
247
253
  * Boolean indicating whether, when the Android default back button is clicked, the `pop` action should be performed on the native side or on the JS side to be able to prevent it.
248
254
  * Unfortunately the same behavior is not available on iOS since the behavior of native back button cannot be changed there.
@@ -251,6 +257,18 @@ export type NativeStackNavigationOptions = {
251
257
  * @platform android
252
258
  */
253
259
  nativeBackButtonDismissalEnabled?: boolean;
260
+ /**
261
+ * Sets the navigation bar color. Defaults to initial status bar color.
262
+ *
263
+ * @platform android
264
+ */
265
+ navigationBarColor?: string;
266
+ /**
267
+ * Sets the visibility of the navigation bar. Defaults to `false`.
268
+ *
269
+ * @platform android
270
+ */
271
+ navigationBarHidden?: boolean;
254
272
  /**
255
273
  * How should the screen replacing another screen animate. Defaults to `pop`.
256
274
  * The following values are currently supported:
@@ -273,8 +291,6 @@ export type NativeStackNavigationOptions = {
273
291
  screenOrientation?: ScreenProps['screenOrientation'];
274
292
  /**
275
293
  * Object in which you should pass props in order to render native iOS searchBar.
276
- *
277
- * @platform ios
278
294
  */
279
295
  searchBar?: SearchBarProps;
280
296
  /**
@@ -327,10 +343,26 @@ export type NativeStackNavigationOptions = {
327
343
  * @platform android
328
344
  */
329
345
  statusBarTranslucent?: boolean;
346
+ /**
347
+ * Sets the direction in which you should swipe to dismiss the screen.
348
+ * When using `vertical` option, options `fullScreenSwipeEnabled: true`, `customAnimationOnSwipe: true` and `stackAnimation: 'slide_from_bottom'` are set by default.
349
+ * The following values are supported:
350
+ * - `vertical` – dismiss screen vertically
351
+ * - `horizontal` – dismiss screen horizontally (default)
352
+ * @platform ios
353
+ */
354
+ swipeDirection?: ScreenProps['swipeDirection'];
330
355
  /**
331
356
  * String that can be displayed in the header as a fallback for `headerTitle`.
332
357
  */
333
358
  title?: string;
359
+ /**
360
+ * Changes the duration (in milliseconds) of `slide_from_bottom`, `fade_from_bottom`, `fade` and `simple_push` transitions on iOS. Defaults to `350`.
361
+ * The duration of `default` and `flip` transitions isn't customizable.
362
+ *
363
+ * @platform ios
364
+ */
365
+ transitionDuration?: number;
334
366
  };
335
367
 
336
368
  export type NativeStackNavigatorProps = DefaultNavigatorOptions<