diff --git a/packages/stack/src/types.tsx b/packages/stack/src/types.tsx index 28ed6dcc..d5c17ffb 100644 --- a/packages/stack/src/types.tsx +++ b/packages/stack/src/types.tsx @@ -5,6 +5,7 @@ import { LayoutChangeEvent, } from 'react-native'; import Animated from 'react-native-reanimated'; +import { EdgeInsets } from 'react-native-safe-area-context'; import { NavigationProp, ParamListBase, @@ -207,6 +208,10 @@ export type StackHeaderProps = { * Layout of the screen. */ layout: Layout; + /** + * Safe area insets to use in the header, e.g. to apply extra spacing for statusbar and notch. + */ + insets: EdgeInsets; /** * Object representing the current scene, such as the route object and animation progress. */ @@ -302,6 +307,17 @@ export type StackNavigationOptions = StackHeaderOptions & * Defaults to 0.3. */ gestureVelocityImpact?: number; + /** + * Safe area insets for the screen. This is used to avoid elements like notch and status bar. + * By default, the device's safe area insets are automatically detected. You can override the behavior with this option. + * For example, to remove the extra spacing for status bar, pass `safeAreaInsets: { top: 0 }`. + */ + safeAreaInsets?: { + top?: number; + right?: number; + bottom?: number; + left?: number; + }; }; export type StackNavigationConfig = { diff --git a/packages/stack/src/views/Header/Header.tsx b/packages/stack/src/views/Header/Header.tsx index 5953a4a1..9eca1180 100644 --- a/packages/stack/src/views/Header/Header.tsx +++ b/packages/stack/src/views/Header/Header.tsx @@ -1,15 +1,19 @@ import * as React from 'react'; import { StackActions } from '@react-navigation/routers'; -import { useSafeArea } from 'react-native-safe-area-context'; import HeaderSegment from './HeaderSegment'; import { StackHeaderProps, StackHeaderTitleProps } from '../../types'; import HeaderTitle from './HeaderTitle'; export default React.memo(function Header(props: StackHeaderProps) { - const insets = useSafeArea(); - - const { scene, previous, layout, navigation, styleInterpolator } = props; + const { + scene, + previous, + layout, + insets, + navigation, + styleInterpolator, + } = props; const { options } = scene.descriptor; const title = typeof options.headerTitle !== 'function' && diff --git a/packages/stack/src/views/Header/HeaderContainer.tsx b/packages/stack/src/views/Header/HeaderContainer.tsx index d71072ba..93cf10de 100644 --- a/packages/stack/src/views/Header/HeaderContainer.tsx +++ b/packages/stack/src/views/Header/HeaderContainer.tsx @@ -6,6 +6,7 @@ import { ParamListBase, } from '@react-navigation/core'; import { StackNavigationState } from '@react-navigation/routers'; +import { EdgeInsets } from 'react-native-safe-area-context'; import Header from './Header'; import { forStatic } from '../../TransitionConfigs/HeaderStyleInterpolators'; @@ -19,6 +20,7 @@ import { export type Props = { mode: 'float' | 'screen'; layout: Layout; + insets: EdgeInsets; scenes: Array> | undefined>; state: StackNavigationState; getPreviousRoute: (props: { @@ -36,6 +38,7 @@ export default function HeaderContainer({ mode, scenes, layout, + insets, state, getPreviousRoute, onContentHeightChange, @@ -87,6 +90,7 @@ export default function HeaderContainer({ const props = { mode, layout, + insets, scene, previous, navigation: scene.descriptor.navigation as StackNavigationProp< diff --git a/packages/stack/src/views/Stack/Card.tsx b/packages/stack/src/views/Stack/Card.tsx index 0e38e98f..2fa65fd2 100755 --- a/packages/stack/src/views/Stack/Card.tsx +++ b/packages/stack/src/views/Stack/Card.tsx @@ -385,7 +385,10 @@ export default class Card extends React.Component { this.props.current, this.props.next, this.props.layout, - this.props.insets + this.props.insets.top, + this.props.insets.right, + this.props.insets.bottom, + this.props.insets.left ); }; @@ -684,7 +687,10 @@ export default class Card extends React.Component { current: Animated.Node, next: Animated.Node | undefined, layout: Layout, - insets: EdgeInsets + insetTop: number, + insetRight: number, + insetBottom: number, + insetLeft: number ) => styleInterpolator({ index, @@ -694,7 +700,12 @@ export default class Card extends React.Component { layouts: { screen: layout, }, - insets, + insets: { + top: insetTop, + right: insetRight, + bottom: insetBottom, + left: insetLeft, + }, }) ); @@ -708,7 +719,10 @@ export default class Card extends React.Component { this.props.current, this.props.next, this.props.layout, - this.props.insets + this.props.insets.top, + this.props.insets.right, + this.props.insets.bottom, + this.props.insets.left ); private gestureActivationCriteria() { @@ -776,7 +790,10 @@ export default class Card extends React.Component { current, next, layout, - insets + insets.top, + insets.right, + insets.bottom, + insets.left ); } diff --git a/packages/stack/src/views/Stack/Stack.tsx b/packages/stack/src/views/Stack/Stack.tsx index a1e2b86b..1754849c 100755 --- a/packages/stack/src/views/Stack/Stack.tsx +++ b/packages/stack/src/views/Stack/Stack.tsx @@ -332,6 +332,13 @@ export default class Stack extends React.Component { }; } + const { + top = insets.top, + right = insets.right, + bottom = insets.bottom, + left = insets.left, + } = focusedOptions.safeAreaInsets || {}; + return ( { : 0; const { + safeAreaInsets, headerShown, headerTransparent, cardTransparent, @@ -406,6 +414,13 @@ export default class Stack extends React.Component { } } + const { + top: safeAreaInsetTop = insets.top, + right: safeAreaInsetRight = insets.right, + bottom: safeAreaInsetBottom = insets.bottom, + left: safeAreaInsetLeft = insets.left, + } = safeAreaInsets || {}; + return ( { focused={focused} closing={closingRouteKeys.includes(route.key)} layout={layout} - insets={insets} current={current} scene={scene} previousScene={scenes[index - 1]} navigation={navigation} state={state} + safeAreaInsetTop={safeAreaInsetTop} + safeAreaInsetRight={safeAreaInsetRight} + safeAreaInsetBottom={safeAreaInsetBottom} + safeAreaInsetLeft={safeAreaInsetLeft} cardTransparent={cardTransparent} cardOverlayEnabled={cardOverlayEnabled} cardShadowEnabled={cardShadowEnabled} @@ -459,6 +477,7 @@ export default class Stack extends React.Component { ? renderHeader({ mode: 'float', layout, + insets: { top, right, bottom, left }, scenes, state, getPreviousRoute, diff --git a/packages/stack/src/views/Stack/StackItem.tsx b/packages/stack/src/views/Stack/StackItem.tsx index 016d24b1..31dd36ce 100644 --- a/packages/stack/src/views/Stack/StackItem.tsx +++ b/packages/stack/src/views/Stack/StackItem.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native'; import Animated from 'react-native-reanimated'; -import { EdgeInsets } from 'react-native-safe-area-context'; import { StackNavigationState } from '@react-navigation/routers'; import { Route } from '@react-navigation/core'; import { Props as HeaderContainerProps } from '../Header/HeaderContainer'; @@ -20,12 +19,15 @@ type Props = TransitionPreset & { focused: boolean; closing: boolean; layout: Layout; - insets: EdgeInsets; current: Animated.Value; previousScene?: Scene>; scene: Scene>; state: StackNavigationState; navigation: StackNavigationHelpers; + safeAreaInsetTop: number; + safeAreaInsetRight: number; + safeAreaInsetBottom: number; + safeAreaInsetLeft: number; cardTransparent?: boolean; cardOverlayEnabled?: boolean; cardShadowEnabled?: boolean; @@ -96,7 +98,6 @@ export default class StackItem extends React.PureComponent { const { index, layout, - insets, active, focused, closing, @@ -104,6 +105,10 @@ export default class StackItem extends React.PureComponent { state, scene, previousScene, + safeAreaInsetTop, + safeAreaInsetRight, + safeAreaInsetBottom, + safeAreaInsetLeft, cardTransparent, cardOverlayEnabled, cardShadowEnabled, @@ -126,6 +131,13 @@ export default class StackItem extends React.PureComponent { headerStyleInterpolator, } = this.props; + const insets = { + top: safeAreaInsetTop, + right: safeAreaInsetRight, + bottom: safeAreaInsetBottom, + left: safeAreaInsetLeft, + }; + return ( { ? renderHeader({ mode: 'screen', layout, + insets, scenes: [previousScene, scene], state, getPreviousRoute,