@react-navigation/native-stack 6.2.4 → 6.4.1

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 (30) hide show
  1. package/lib/commonjs/index.js +8 -0
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/navigators/createNativeStackNavigator.js +12 -10
  4. package/lib/commonjs/navigators/createNativeStackNavigator.js.map +1 -1
  5. package/lib/commonjs/views/HeaderConfig.js +62 -39
  6. package/lib/commonjs/views/HeaderConfig.js.map +1 -1
  7. package/lib/commonjs/views/NativeStackView.js +62 -40
  8. package/lib/commonjs/views/NativeStackView.js.map +1 -1
  9. package/lib/commonjs/views/NativeStackView.native.js +51 -40
  10. package/lib/commonjs/views/NativeStackView.native.js.map +1 -1
  11. package/lib/module/index.js +5 -0
  12. package/lib/module/index.js.map +1 -1
  13. package/lib/module/navigators/createNativeStackNavigator.js +12 -10
  14. package/lib/module/navigators/createNativeStackNavigator.js.map +1 -1
  15. package/lib/module/views/HeaderConfig.js +62 -39
  16. package/lib/module/views/HeaderConfig.js.map +1 -1
  17. package/lib/module/views/NativeStackView.js +61 -40
  18. package/lib/module/views/NativeStackView.js.map +1 -1
  19. package/lib/module/views/NativeStackView.native.js +53 -42
  20. package/lib/module/views/NativeStackView.native.js.map +1 -1
  21. package/lib/typescript/src/index.d.ts +5 -1
  22. package/lib/typescript/src/types.d.ts +19 -10
  23. package/lib/typescript/src/views/HeaderConfig.d.ts +2 -1
  24. package/package.json +6 -6
  25. package/src/index.tsx +6 -0
  26. package/src/navigators/createNativeStackNavigator.tsx +21 -18
  27. package/src/types.tsx +20 -8
  28. package/src/views/HeaderConfig.tsx +153 -103
  29. package/src/views/NativeStackView.native.tsx +61 -47
  30. package/src/views/NativeStackView.tsx +25 -3
@@ -25,18 +25,19 @@ function NativeStackNavigator({
25
25
  screenOptions,
26
26
  ...rest
27
27
  }: NativeStackNavigatorProps) {
28
- const { state, descriptors, navigation } = useNavigationBuilder<
29
- StackNavigationState<ParamListBase>,
30
- StackRouterOptions,
31
- StackActionHelpers<ParamListBase>,
32
- NativeStackNavigationOptions,
33
- NativeStackNavigationEventMap
34
- >(StackRouter, {
35
- initialRouteName,
36
- children,
37
- screenListeners,
38
- screenOptions,
39
- });
28
+ const { state, descriptors, navigation, NavigationContent } =
29
+ useNavigationBuilder<
30
+ StackNavigationState<ParamListBase>,
31
+ StackRouterOptions,
32
+ StackActionHelpers<ParamListBase>,
33
+ NativeStackNavigationOptions,
34
+ NativeStackNavigationEventMap
35
+ >(StackRouter, {
36
+ initialRouteName,
37
+ children,
38
+ screenListeners,
39
+ screenOptions,
40
+ });
40
41
 
41
42
  React.useEffect(
42
43
  () =>
@@ -64,12 +65,14 @@ function NativeStackNavigator({
64
65
  );
65
66
 
66
67
  return (
67
- <NativeStackView
68
- {...rest}
69
- state={state}
70
- navigation={navigation}
71
- descriptors={descriptors}
72
- />
68
+ <NavigationContent>
69
+ <NativeStackView
70
+ {...rest}
71
+ state={state}
72
+ navigation={navigation}
73
+ descriptors={descriptors}
74
+ />
75
+ </NavigationContent>
73
76
  );
74
77
  }
75
78
 
package/src/types.tsx CHANGED
@@ -85,20 +85,23 @@ export type NativeStackHeaderProps = {
85
85
  navigation: NativeStackNavigationProp<ParamListBase>;
86
86
  };
87
87
 
88
- export type HeaderBackButtonProps = {
88
+ export type HeaderButtonProps = {
89
89
  /**
90
90
  * Tint color for the header.
91
91
  */
92
92
  tintColor?: string;
93
+ /**
94
+ * Whether it's possible to navigate back in stack.
95
+ */
96
+ canGoBack: boolean;
97
+ };
98
+
99
+ export type HeaderBackButtonProps = HeaderButtonProps & {
93
100
  /**
94
101
  * Label text for the button. Usually the title of the previous screen.
95
102
  * By default, this is only shown on iOS.
96
103
  */
97
104
  label?: string;
98
- /**
99
- * Whether it's possible to navigate back in stack.
100
- */
101
- canGoBack: boolean;
102
105
  };
103
106
 
104
107
  export type NativeStackNavigationOptions = {
@@ -179,6 +182,7 @@ export type NativeStackNavigationOptions = {
179
182
  *
180
183
  * For large title to collapse on scroll, the content of the screen should be wrapped in a scrollable view such as `ScrollView` or `FlatList`.
181
184
  * If the scrollable area doesn't fill the screen, the large title won't collapse on scroll.
185
+ * You also need to specify `contentInsetAdjustmentBehavior="automatic"` in your `ScrollView`, `FlatList` etc.
182
186
  *
183
187
  * Only supported on iOS.
184
188
  *
@@ -245,6 +249,12 @@ export type NativeStackNavigationOptions = {
245
249
  * Tint color for the header. Changes the color of back button and title.
246
250
  */
247
251
  headerTintColor?: string;
252
+ /**
253
+ * Function which returns a React Element to render as the background of the header.
254
+ * This is useful for using backgrounds such as an image, a gradient, blur effect etc.
255
+ * You can use this with `headerTransparent` to render content underneath a translucent header.
256
+ */
257
+ headerBackground?: () => React.ReactNode;
248
258
  /**
249
259
  * Function which returns a React Element to display on the left side of the header.
250
260
  * This replaces the back button. See `headerBackVisible` to show the back button along side left element.
@@ -253,7 +263,7 @@ export type NativeStackNavigationOptions = {
253
263
  /**
254
264
  * Function which returns a React Element to display on the right side of the header.
255
265
  */
256
- headerRight?: (props: { tintColor?: string }) => React.ReactNode;
266
+ headerRight?: (props: HeaderButtonProps) => React.ReactNode;
257
267
  /**
258
268
  * String or a function that returns a React Element to be used by the header.
259
269
  * Defaults to screen `title` or route name.
@@ -295,9 +305,11 @@ export type NativeStackNavigationOptions = {
295
305
  }
296
306
  >;
297
307
  /**
298
- * Options to render a native search bar on iOS.
308
+ * Options to render a native search bar.
309
+ * You also need to specify `contentInsetAdjustmentBehavior="automatic"` in your `ScrollView`, `FlatList` etc.
310
+ * If you don't have a `ScrollView`, specify `headerTransparent: false`.
299
311
  *
300
- * @platform ios
312
+ * Only supported on iOS and Android.
301
313
  */
302
314
  headerSearchBarOptions?: SearchBarProps;
303
315
  /**
@@ -10,6 +10,7 @@ import {
10
10
  } from 'react-native';
11
11
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
12
12
  import {
13
+ isSearchBarAvailableForCurrentPlatform,
13
14
  ScreenStackHeaderBackButtonImage,
14
15
  ScreenStackHeaderCenterView,
15
16
  ScreenStackHeaderConfig,
@@ -23,11 +24,13 @@ import type { NativeStackNavigationOptions } from '../types';
23
24
  import { processFonts } from './FontProcessor';
24
25
 
25
26
  type Props = NativeStackNavigationOptions & {
27
+ headerHeight: number;
26
28
  route: Route<string>;
27
29
  canGoBack: boolean;
28
30
  };
29
31
 
30
32
  export default function HeaderConfig({
33
+ headerHeight,
31
34
  headerBackImageSource,
32
35
  headerBackButtonMenuEnabled,
33
36
  headerBackTitle,
@@ -39,6 +42,7 @@ export default function HeaderConfig({
39
42
  headerLargeTitle,
40
43
  headerLargeTitleShadowVisible,
41
44
  headerLargeTitleStyle,
45
+ headerBackground,
42
46
  headerLeft,
43
47
  headerRight,
44
48
  headerShown,
@@ -55,6 +59,7 @@ export default function HeaderConfig({
55
59
  canGoBack,
56
60
  }: Props): JSX.Element {
57
61
  const insets = useSafeAreaInsets();
62
+
58
63
  const { colors } = useTheme();
59
64
  const tintColor =
60
65
  headerTintColor ?? (Platform.OS === 'ios' ? colors.primary : colors.text);
@@ -96,20 +101,28 @@ export default function HeaderConfig({
96
101
 
97
102
  const headerLeftElement = headerLeft?.({
98
103
  tintColor,
104
+ canGoBack,
99
105
  label: headerBackTitle,
106
+ });
107
+ const headerRightElement = headerRight?.({
108
+ tintColor,
100
109
  canGoBack,
101
110
  });
102
- const headerRightElement = headerRight?.({ tintColor });
103
111
  const headerTitleElement =
104
112
  typeof headerTitle === 'function'
105
113
  ? headerTitle({ tintColor, children: titleText })
106
114
  : null;
107
115
 
108
- if (
109
- Platform.OS === 'ios' &&
110
- headerSearchBarOptions != null &&
111
- SearchBar == null
112
- ) {
116
+ const supportsHeaderSearchBar =
117
+ typeof isSearchBarAvailableForCurrentPlatform === 'boolean'
118
+ ? isSearchBarAvailableForCurrentPlatform
119
+ : // Fallback for older versions of react-native-screens
120
+ Platform.OS === 'ios' && SearchBar != null;
121
+
122
+ const hasHeaderSearchBar =
123
+ supportsHeaderSearchBar && headerSearchBarOptions != null;
124
+
125
+ if (headerSearchBarOptions != null && !supportsHeaderSearchBar) {
113
126
  throw new Error(
114
127
  `The current version of 'react-native-screens' doesn't support SearchBar in the header. Please update to the latest version to use this option.`
115
128
  );
@@ -124,105 +137,132 @@ export default function HeaderConfig({
124
137
  ? headerLeftElement != null
125
138
  : Platform.OS === 'android' && headerTitleElement != null;
126
139
 
140
+ const translucent =
141
+ headerBackground != null ||
142
+ headerTransparent ||
143
+ // When using a SearchBar or large title, the header needs to be translucent for it to work on iOS
144
+ ((hasHeaderSearchBar || headerLargeTitle) &&
145
+ Platform.OS === 'ios' &&
146
+ headerTransparent !== false);
147
+
127
148
  return (
128
- <ScreenStackHeaderConfig
129
- backButtonInCustomView={backButtonInCustomView}
130
- backgroundColor={
131
- headerStyleFlattened.backgroundColor ??
132
- (headerTransparent ? 'transparent' : colors.card)
133
- }
134
- backTitle={headerBackTitleVisible ? headerBackTitle : ' '}
135
- backTitleFontFamily={backTitleFontFamily}
136
- backTitleFontSize={headerBackTitleStyleFlattened.fontSize}
137
- blurEffect={headerBlurEffect}
138
- color={tintColor}
139
- direction={I18nManager.isRTL ? 'rtl' : 'ltr'}
140
- disableBackButtonMenu={headerBackButtonMenuEnabled === false}
141
- hidden={headerShown === false}
142
- hideBackButton={headerBackVisible === false}
143
- hideShadow={headerShadowVisible === false}
144
- largeTitle={headerLargeTitle}
145
- largeTitleBackgroundColor={headerLargeStyleFlattened.backgroundColor}
146
- largeTitleColor={headerLargeTitleStyleFlattened.color}
147
- largeTitleFontFamily={largeTitleFontFamily}
148
- largeTitleFontSize={headerLargeTitleStyleFlattened.fontSize}
149
- largeTitleFontWeight={headerLargeTitleStyleFlattened.fontWeight}
150
- largeTitleHideShadow={headerLargeTitleShadowVisible === false}
151
- title={typeof headerTitle === 'string' ? headerTitle : titleText}
152
- titleColor={titleColor}
153
- titleFontFamily={titleFontFamily}
154
- titleFontSize={titleFontSize}
155
- titleFontWeight={titleFontWeight}
156
- topInsetEnabled={insets.top !== 0}
157
- translucent={
158
- // This defaults to `true`, so we can't pass `undefined`
159
- headerTransparent === true
160
- }
161
- >
162
- {Platform.OS === 'ios' ? (
163
- <>
164
- {headerLeftElement != null ? (
165
- <ScreenStackHeaderLeftView>
166
- {headerLeftElement}
167
- </ScreenStackHeaderLeftView>
168
- ) : null}
169
- {headerTitleElement != null ? (
170
- <ScreenStackHeaderCenterView>
171
- {headerTitleElement}
172
- </ScreenStackHeaderCenterView>
173
- ) : null}
174
- </>
175
- ) : (
176
- <>
177
- {headerLeftElement != null || typeof headerTitle === 'function' ? (
178
- <ScreenStackHeaderLeftView>
179
- <View style={styles.row}>
180
- {headerLeftElement}
181
- {headerTitleAlign !== 'center' ? (
182
- typeof headerTitle === 'function' ? (
183
- headerTitleElement
184
- ) : (
185
- <HeaderTitle
186
- tintColor={tintColor}
187
- style={headerTitleStyleSupported}
188
- >
189
- {titleText}
190
- </HeaderTitle>
191
- )
192
- ) : null}
193
- </View>
194
- </ScreenStackHeaderLeftView>
195
- ) : null}
196
- {headerTitleAlign === 'center' ? (
197
- <ScreenStackHeaderCenterView>
198
- {typeof headerTitle === 'function' ? (
199
- headerTitleElement
200
- ) : (
201
- <HeaderTitle
202
- tintColor={tintColor}
203
- style={headerTitleStyleSupported}
204
- >
205
- {titleText}
206
- </HeaderTitle>
207
- )}
208
- </ScreenStackHeaderCenterView>
209
- ) : null}
210
- </>
211
- )}
212
- {headerBackImageSource !== undefined ? (
213
- <ScreenStackHeaderBackButtonImage source={headerBackImageSource} />
214
- ) : null}
215
- {headerRightElement != null ? (
216
- <ScreenStackHeaderRightView>
217
- {headerRightElement}
218
- </ScreenStackHeaderRightView>
219
- ) : null}
220
- {Platform.OS === 'ios' && headerSearchBarOptions != null ? (
221
- <ScreenStackHeaderSearchBarView>
222
- <SearchBar {...headerSearchBarOptions} />
223
- </ScreenStackHeaderSearchBarView>
149
+ <>
150
+ {headerBackground != null ? (
151
+ <View
152
+ style={[
153
+ styles.background,
154
+ headerTransparent ? styles.translucent : null,
155
+ { height: headerHeight },
156
+ ]}
157
+ >
158
+ {headerBackground()}
159
+ </View>
224
160
  ) : null}
225
- </ScreenStackHeaderConfig>
161
+ <ScreenStackHeaderConfig
162
+ backButtonInCustomView={backButtonInCustomView}
163
+ backgroundColor={
164
+ headerStyleFlattened.backgroundColor ??
165
+ (headerBackground != null || headerTransparent
166
+ ? 'transparent'
167
+ : colors.card)
168
+ }
169
+ backTitle={headerBackTitleVisible ? headerBackTitle : ' '}
170
+ backTitleFontFamily={backTitleFontFamily}
171
+ backTitleFontSize={headerBackTitleStyleFlattened.fontSize}
172
+ blurEffect={headerBlurEffect}
173
+ color={tintColor}
174
+ direction={I18nManager.isRTL ? 'rtl' : 'ltr'}
175
+ disableBackButtonMenu={headerBackButtonMenuEnabled === false}
176
+ hidden={headerShown === false}
177
+ hideBackButton={headerBackVisible === false}
178
+ hideShadow={
179
+ headerShadowVisible === false ||
180
+ headerBackground != null ||
181
+ headerTransparent
182
+ }
183
+ largeTitle={headerLargeTitle}
184
+ largeTitleBackgroundColor={headerLargeStyleFlattened.backgroundColor}
185
+ largeTitleColor={headerLargeTitleStyleFlattened.color}
186
+ largeTitleFontFamily={largeTitleFontFamily}
187
+ largeTitleFontSize={headerLargeTitleStyleFlattened.fontSize}
188
+ largeTitleFontWeight={headerLargeTitleStyleFlattened.fontWeight}
189
+ largeTitleHideShadow={headerLargeTitleShadowVisible === false}
190
+ title={typeof headerTitle === 'string' ? headerTitle : titleText}
191
+ titleColor={titleColor}
192
+ titleFontFamily={titleFontFamily}
193
+ titleFontSize={titleFontSize}
194
+ titleFontWeight={titleFontWeight}
195
+ topInsetEnabled={insets.top !== 0}
196
+ translucent={
197
+ // This defaults to `true`, so we can't pass `undefined`
198
+ translucent === true
199
+ }
200
+ >
201
+ {Platform.OS === 'ios' ? (
202
+ <>
203
+ {headerLeftElement != null ? (
204
+ <ScreenStackHeaderLeftView>
205
+ {headerLeftElement}
206
+ </ScreenStackHeaderLeftView>
207
+ ) : null}
208
+ {headerTitleElement != null ? (
209
+ <ScreenStackHeaderCenterView>
210
+ {headerTitleElement}
211
+ </ScreenStackHeaderCenterView>
212
+ ) : null}
213
+ </>
214
+ ) : (
215
+ <>
216
+ {headerLeftElement != null || typeof headerTitle === 'function' ? (
217
+ <ScreenStackHeaderLeftView>
218
+ <View style={styles.row}>
219
+ {headerLeftElement}
220
+ {headerTitleAlign !== 'center' ? (
221
+ typeof headerTitle === 'function' ? (
222
+ headerTitleElement
223
+ ) : (
224
+ <HeaderTitle
225
+ tintColor={tintColor}
226
+ style={headerTitleStyleSupported}
227
+ >
228
+ {titleText}
229
+ </HeaderTitle>
230
+ )
231
+ ) : null}
232
+ </View>
233
+ </ScreenStackHeaderLeftView>
234
+ ) : null}
235
+ {headerTitleAlign === 'center' ? (
236
+ <ScreenStackHeaderCenterView>
237
+ {typeof headerTitle === 'function' ? (
238
+ headerTitleElement
239
+ ) : (
240
+ <HeaderTitle
241
+ tintColor={tintColor}
242
+ style={headerTitleStyleSupported}
243
+ >
244
+ {titleText}
245
+ </HeaderTitle>
246
+ )}
247
+ </ScreenStackHeaderCenterView>
248
+ ) : null}
249
+ </>
250
+ )}
251
+ {headerBackImageSource !== undefined ? (
252
+ <ScreenStackHeaderBackButtonImage source={headerBackImageSource} />
253
+ ) : null}
254
+ {headerRightElement != null ? (
255
+ <ScreenStackHeaderRightView>
256
+ {headerRightElement}
257
+ </ScreenStackHeaderRightView>
258
+ ) : null}
259
+ {hasHeaderSearchBar ? (
260
+ <ScreenStackHeaderSearchBarView>
261
+ <SearchBar {...headerSearchBarOptions} />
262
+ </ScreenStackHeaderSearchBarView>
263
+ ) : null}
264
+ </ScreenStackHeaderConfig>
265
+ </>
226
266
  );
227
267
  }
228
268
 
@@ -231,4 +271,14 @@ const styles = StyleSheet.create({
231
271
  flexDirection: 'row',
232
272
  alignItems: 'center',
233
273
  },
274
+ translucent: {
275
+ position: 'absolute',
276
+ top: 0,
277
+ left: 0,
278
+ right: 0,
279
+ zIndex: 1,
280
+ },
281
+ background: {
282
+ overflow: 'hidden',
283
+ },
234
284
  });
@@ -6,6 +6,8 @@ import {
6
6
  SafeAreaProviderCompat,
7
7
  } from '@react-navigation/elements';
8
8
  import {
9
+ NavigationContext,
10
+ NavigationRouteContext,
9
11
  ParamListBase,
10
12
  Route,
11
13
  StackActions,
@@ -13,7 +15,7 @@ import {
13
15
  useTheme,
14
16
  } from '@react-navigation/native';
15
17
  import * as React from 'react';
16
- import { Platform, PlatformIOSStatic, StyleSheet } from 'react-native';
18
+ import { Platform, StyleSheet, View } from 'react-native';
17
19
  import {
18
20
  useSafeAreaFrame,
19
21
  useSafeAreaInsets,
@@ -40,11 +42,13 @@ const MaybeNestedStack = ({
40
42
  options,
41
43
  route,
42
44
  presentation,
45
+ headerHeight,
43
46
  children,
44
47
  }: {
45
48
  options: NativeStackNavigationOptions;
46
49
  route: Route<string>;
47
50
  presentation: Exclude<StackPresentationTypes, 'push'> | 'card';
51
+ headerHeight: number;
48
52
  children: React.ReactNode;
49
53
  }) => {
50
54
  const { colors } = useTheme();
@@ -83,33 +87,17 @@ const MaybeNestedStack = ({
83
87
  </DebugContainer>
84
88
  );
85
89
 
86
- const insets = useSafeAreaInsets();
87
- const dimensions = useSafeAreaFrame();
88
- // landscape is meaningful only for iPhone
89
- const isLandscape =
90
- dimensions.width > dimensions.height &&
91
- !(Platform as PlatformIOSStatic).isPad &&
92
- !(Platform as PlatformIOSStatic).isTVOS;
93
- // `modal` and `formSheet` presentations do not take whole screen, so should not take the inset.
94
- const isFullScreenModal =
95
- presentation !== 'modal' && presentation !== 'formSheet';
96
- const topInset = isFullScreenModal && !isLandscape ? insets.top : 0;
97
- const headerHeight = getDefaultHeaderHeight(
98
- dimensions,
99
- !isFullScreenModal,
100
- topInset
101
- );
102
-
103
90
  if (isHeaderInModal) {
104
91
  return (
105
92
  <ScreenStack style={styles.container}>
106
93
  <Screen enabled style={StyleSheet.absoluteFill}>
107
- <HeaderShownContext.Provider value>
108
- <HeaderHeightContext.Provider value={headerHeight}>
109
- <HeaderConfig {...options} route={route} canGoBack />
110
- {content}
111
- </HeaderHeightContext.Provider>
112
- </HeaderShownContext.Provider>
94
+ <HeaderConfig
95
+ {...options}
96
+ route={route}
97
+ headerHeight={headerHeight}
98
+ canGoBack
99
+ />
100
+ {content}
113
101
  </Screen>
114
102
  </ScreenStack>
115
103
  );
@@ -142,7 +130,7 @@ const SceneView = ({
142
130
  gestureEnabled,
143
131
  header,
144
132
  headerShown,
145
- animationTypeForReplace = 'pop',
133
+ animationTypeForReplace = 'push',
146
134
  animation,
147
135
  orientation,
148
136
  statusBarAnimation,
@@ -162,14 +150,28 @@ const SceneView = ({
162
150
  ? headerShown
163
151
  : presentation === 'card' && headerShown !== false;
164
152
 
165
- const isParentHeaderShown = React.useContext(HeaderShownContext);
166
153
  const insets = useSafeAreaInsets();
154
+ const frame = useSafeAreaFrame();
155
+
156
+ // `modal` and `formSheet` presentations do not take whole screen, so should not take the inset.
157
+ const isModal = presentation === 'modal' || presentation === 'formSheet';
158
+
159
+ // Modals are fullscreen in landscape only on iPhone
160
+ const isIPhone =
161
+ Platform.OS === 'ios' && !(Platform.isPad && Platform.isTVOS);
162
+ const isLandscape = frame.width > frame.height;
163
+
164
+ const topInset = isModal || (isIPhone && isLandscape) ? 0 : insets.top;
165
+
166
+ const isParentHeaderShown = React.useContext(HeaderShownContext);
167
167
  const parentHeaderHeight = React.useContext(HeaderHeightContext);
168
- const headerHeight = getDefaultHeaderHeight(
169
- useSafeAreaFrame(),
170
- false,
171
- insets.top
172
- );
168
+
169
+ const defaultHeaderHeight = getDefaultHeaderHeight(frame, isModal, topInset);
170
+
171
+ const [customHeaderHeight, setCustomHeaderHeight] =
172
+ React.useState(defaultHeaderHeight);
173
+
174
+ const headerHeight = header ? customHeaderHeight : defaultHeaderHeight;
173
175
 
174
176
  return (
175
177
  <Screen
@@ -204,25 +206,35 @@ const SceneView = ({
204
206
  }
205
207
  >
206
208
  {header !== undefined && headerShown !== false ? (
207
- // TODO: expose custom header height
208
- header({
209
- back: previousDescriptor
210
- ? {
211
- title: getHeaderTitle(
212
- previousDescriptor.options,
213
- previousDescriptor.route.name
214
- ),
215
- }
216
- : undefined,
217
- options,
218
- route,
219
- navigation,
220
- })
209
+ <NavigationContext.Provider value={navigation}>
210
+ <NavigationRouteContext.Provider value={route}>
211
+ <View
212
+ onLayout={(e) => {
213
+ setCustomHeaderHeight(e.nativeEvent.layout.height);
214
+ }}
215
+ >
216
+ {header({
217
+ back: previousDescriptor
218
+ ? {
219
+ title: getHeaderTitle(
220
+ previousDescriptor.options,
221
+ previousDescriptor.route.name
222
+ ),
223
+ }
224
+ : undefined,
225
+ options,
226
+ route,
227
+ navigation,
228
+ })}
229
+ </View>
230
+ </NavigationRouteContext.Provider>
231
+ </NavigationContext.Provider>
221
232
  ) : (
222
233
  <HeaderConfig
223
234
  {...options}
224
235
  route={route}
225
236
  headerShown={isHeaderInPush}
237
+ headerHeight={headerHeight}
226
238
  canGoBack={index !== 0}
227
239
  />
228
240
  )}
@@ -230,6 +242,7 @@ const SceneView = ({
230
242
  options={options}
231
243
  route={route}
232
244
  presentation={presentation}
245
+ headerHeight={headerHeight}
233
246
  >
234
247
  {render()}
235
248
  </MaybeNestedStack>
@@ -246,8 +259,9 @@ type Props = {
246
259
  };
247
260
 
248
261
  function NativeStackViewInner({ state, navigation, descriptors }: Props) {
249
- const [nextDismissedKey, setNextDismissedKey] =
250
- React.useState<string | null>(null);
262
+ const [nextDismissedKey, setNextDismissedKey] = React.useState<string | null>(
263
+ null
264
+ );
251
265
 
252
266
  const dismissedRouteName = nextDismissedKey
253
267
  ? state.routes.find((route) => route.key === nextDismissedKey)?.name
@@ -25,6 +25,11 @@ type Props = {
25
25
  descriptors: NativeStackDescriptorMap;
26
26
  };
27
27
 
28
+ const TRANSPARENT_PRESENTATIONS = [
29
+ 'transparentModal',
30
+ 'containedTransparentModal',
31
+ ];
32
+
28
33
  export default function NativeStackView({ state, descriptors }: Props) {
29
34
  return (
30
35
  <SafeAreaProviderCompat>
@@ -33,9 +38,11 @@ export default function NativeStackView({ state, descriptors }: Props) {
33
38
  const isFocused = state.index === i;
34
39
  const canGoBack = i !== 0;
35
40
  const previousKey = state.routes[i - 1]?.key;
41
+ const nextKey = state.routes[i + 1]?.key;
36
42
  const previousDescriptor = previousKey
37
43
  ? descriptors[previousKey]
38
44
  : undefined;
45
+ const nexDescriptor = nextKey ? descriptors[nextKey] : undefined;
39
46
  const { options, navigation, render } = descriptors[route.key];
40
47
 
41
48
  const {
@@ -51,10 +58,13 @@ export default function NativeStackView({ state, descriptors }: Props) {
51
58
  headerStyle,
52
59
  headerShadowVisible,
53
60
  headerTransparent,
54
- contentStyle,
55
61
  headerBackTitle,
62
+ presentation,
63
+ contentStyle,
56
64
  } = options;
57
65
 
66
+ const nextPresentation = nexDescriptor?.options.presentation;
67
+
58
68
  return (
59
69
  <Screen
60
70
  key={route.key}
@@ -115,7 +125,8 @@ export default function NativeStackView({ state, descriptors }: Props) {
115
125
  }
116
126
  headerRight={
117
127
  typeof headerRight === 'function'
118
- ? ({ tintColor }) => headerRight({ tintColor })
128
+ ? ({ tintColor }) =>
129
+ headerRight({ tintColor, canGoBack })
119
130
  : headerRight
120
131
  }
121
132
  headerTitle={
@@ -143,7 +154,18 @@ export default function NativeStackView({ state, descriptors }: Props) {
143
154
  }
144
155
  style={[
145
156
  StyleSheet.absoluteFill,
146
- { display: isFocused ? 'flex' : 'none' },
157
+ {
158
+ display:
159
+ isFocused ||
160
+ (nextPresentation != null &&
161
+ TRANSPARENT_PRESENTATIONS.includes(nextPresentation))
162
+ ? 'flex'
163
+ : 'none',
164
+ },
165
+ presentation != null &&
166
+ TRANSPARENT_PRESENTATIONS.includes(presentation)
167
+ ? { backgroundColor: 'transparent' }
168
+ : null,
147
169
  ]}
148
170
  >
149
171
  <View style={[styles.contentContainer, contentStyle]}>