@react-navigation/native#NavigationContext TypeScript Examples
The following examples show how to use
@react-navigation/native#NavigationContext.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: BottomTabBar.tsx From nlw2-proffy with MIT License | 4 votes |
export default function BottomTabBar({
state,
navigation,
descriptors,
activeBackgroundColor,
activeTintColor,
adaptive = true,
allowFontScaling,
inactiveBackgroundColor,
inactiveTintColor,
keyboardHidesTabBar = false,
labelPosition,
labelStyle,
iconStyle,
safeAreaInsets,
showLabel,
style,
tabStyle,
}: Props) {
const { colors } = useTheme();
const buildLink = useLinkBuilder();
const focusedRoute = state.routes[state.index];
const focusedDescriptor = descriptors[focusedRoute.key];
const focusedOptions = focusedDescriptor.options;
const dimensions = useWindowDimensions();
const isKeyboardShown = useIsKeyboardShown();
const shouldShowTabBar =
focusedOptions.tabBarVisible !== false &&
!(keyboardHidesTabBar && isKeyboardShown);
const visibilityAnimationConfigRef = React.useRef(
focusedOptions.tabBarVisibilityAnimationConfig
);
React.useEffect(() => {
visibilityAnimationConfigRef.current =
focusedOptions.tabBarVisibilityAnimationConfig;
});
const [isTabBarHidden, setIsTabBarHidden] = React.useState(!shouldShowTabBar);
const [visible] = React.useState(
() => new Animated.Value(shouldShowTabBar ? 1 : 0)
);
React.useEffect(() => {
const visibilityAnimationConfig = visibilityAnimationConfigRef.current;
if (shouldShowTabBar) {
const animation =
visibilityAnimationConfig?.show?.animation === 'spring'
? Animated.spring
: Animated.timing;
animation(visible, {
toValue: 1,
useNativeDriver,
duration: 250,
...visibilityAnimationConfig?.show?.config,
}).start(({ finished }) => {
if (finished) {
setIsTabBarHidden(false);
}
});
} else {
setIsTabBarHidden(true);
const animation =
visibilityAnimationConfig?.hide?.animation === 'spring'
? Animated.spring
: Animated.timing;
animation(visible, {
toValue: 0,
useNativeDriver,
duration: 200,
...visibilityAnimationConfig?.hide?.config,
}).start();
}
}, [visible, shouldShowTabBar]);
const [layout, setLayout] = React.useState({
height: 0,
width: dimensions.width,
});
const handleLayout = (e: LayoutChangeEvent) => {
const { height, width } = e.nativeEvent.layout;
setLayout((layout) => {
if (height === layout.height && width === layout.width) {
return layout;
} else {
return {
height,
width,
};
}
});
};
const { routes } = state;
const shouldUseHorizontalLabels = () => {
if (labelPosition) {
return labelPosition === 'beside-icon';
}
if (!adaptive) {
return false;
}
if (layout.width >= 768) {
// Screen size matches a tablet
let maxTabItemWidth = DEFAULT_MAX_TAB_ITEM_WIDTH;
const flattenedStyle = StyleSheet.flatten(tabStyle);
if (flattenedStyle) {
if (typeof flattenedStyle.width === 'number') {
maxTabItemWidth = flattenedStyle.width;
} else if (typeof flattenedStyle.maxWidth === 'number') {
maxTabItemWidth = flattenedStyle.maxWidth;
}
}
return routes.length * maxTabItemWidth <= layout.width;
} else {
const isLandscape = dimensions.width > dimensions.height;
return isLandscape;
}
};
const defaultInsets = useSafeArea();
const insets = {
top: safeAreaInsets?.top ?? defaultInsets.top,
right: safeAreaInsets?.right ?? defaultInsets.right,
bottom: safeAreaInsets?.bottom ?? defaultInsets.bottom,
left: safeAreaInsets?.left ?? defaultInsets.left,
};
const paddingBottom = Math.max(
insets.bottom - Platform.select({ ios: 4, default: 0 }),
0
);
return (
<Animated.View
style={[
styles.tabBar,
{
backgroundColor: colors.card,
borderTopColor: colors.border,
},
{
transform: [
{
translateY: visible.interpolate({
inputRange: [0, 1],
outputRange: [layout.height + paddingBottom, 0],
}),
},
],
// Absolutely position the tab bar so that the content is below it
// This is needed to avoid gap at bottom when the tab bar is hidden
position: isTabBarHidden ? 'absolute' : (null as any),
},
{
height: DEFAULT_TABBAR_HEIGHT + paddingBottom,
paddingBottom,
paddingHorizontal: Math.max(insets.left, insets.right),
},
style,
]}
pointerEvents={isTabBarHidden ? 'none' : 'auto'}
>
<View style={styles.content} onLayout={handleLayout}>
{routes.map((route, index) => {
const focused = index === state.index;
const { options } = descriptors[route.key];
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!focused && !event.defaultPrevented) {
navigation.dispatch({
...CommonActions.navigate(route.name),
target: state.key,
});
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const accessibilityLabel =
options.tabBarAccessibilityLabel !== undefined
? options.tabBarAccessibilityLabel
: typeof label === 'string'
? `${label}, tab, ${index + 1} of ${routes.length}`
: undefined;
return (
<NavigationContext.Provider
key={route.key}
value={descriptors[route.key].navigation}
>
<NavigationRouteContext.Provider value={route}>
<BottomTabItem
route={route}
focused={focused}
horizontal={shouldUseHorizontalLabels()}
onPress={onPress}
onLongPress={onLongPress}
accessibilityLabel={accessibilityLabel}
to={buildLink(route.name, route.params)}
testID={options.tabBarTestID}
allowFontScaling={allowFontScaling}
activeTintColor={activeTintColor}
inactiveTintColor={inactiveTintColor}
activeBackgroundColor={activeBackgroundColor}
inactiveBackgroundColor={inactiveBackgroundColor}
button={options.tabBarButton}
icon={options.tabBarIcon}
badge={options.tabBarBadge}
label={label}
showLabel={showLabel}
labelStyle={labelStyle}
iconStyle={iconStyle}
style={tabStyle}
/>
</NavigationRouteContext.Provider>
</NavigationContext.Provider>
);
})}
</View>
</Animated.View>
);
}
Example #2
Source File: HeaderContainer.tsx From nlw2-proffy with MIT License | 4 votes |
export default function HeaderContainer({
mode,
scenes,
layout,
insets,
getPreviousScene,
getFocusedRoute,
onContentHeightChange,
gestureDirection,
styleInterpolator,
style,
}: Props) {
const focusedRoute = getFocusedRoute();
const isParentHeaderShown = React.useContext(HeaderShownContext);
const parentPreviousScene = React.useContext(PreviousSceneContext);
return (
<View pointerEvents="box-none" style={style}>
{scenes.slice(-3).map((scene, i, self) => {
if ((mode === 'screen' && i !== self.length - 1) || !scene) {
return null;
}
const {
header,
headerShown = isParentHeaderShown === false,
headerTransparent,
} = scene.descriptor.options || {};
if (!headerShown) {
return null;
}
const isFocused = focusedRoute.key === scene.route.key;
const previous =
getPreviousScene({ route: scene.route }) ?? parentPreviousScene;
// If the screen is next to a headerless screen, we need to make the header appear static
// This makes the header look like it's moving with the screen
const previousScene = self[i - 1];
const nextScene = self[i + 1];
const {
headerShown: previousHeaderShown = isParentHeaderShown === false,
} = previousScene?.descriptor.options || {};
const { headerShown: nextHeaderShown = isParentHeaderShown === false } =
nextScene?.descriptor.options || {};
const isHeaderStatic =
(previousHeaderShown === false &&
// We still need to animate when coming back from next scene
// A hacky way to check this is if the next scene exists
!nextScene) ||
nextHeaderShown === false;
const props = {
mode,
layout,
insets,
scene,
previous,
navigation: scene.descriptor.navigation as StackNavigationProp<
ParamListBase
>,
styleInterpolator:
mode === 'float'
? isHeaderStatic
? gestureDirection === 'vertical' ||
gestureDirection === 'vertical-inverted'
? forSlideUp
: gestureDirection === 'horizontal-inverted'
? forSlideRight
: forSlideLeft
: styleInterpolator
: forNoAnimation,
};
return (
<NavigationContext.Provider
key={scene.route.key}
value={scene.descriptor.navigation}
>
<NavigationRouteContext.Provider value={scene.route}>
<View
onLayout={
onContentHeightChange
? (e) =>
onContentHeightChange({
route: scene.route,
height: e.nativeEvent.layout.height,
})
: undefined
}
pointerEvents={isFocused ? 'box-none' : 'none'}
accessibilityElementsHidden={!isFocused}
importantForAccessibility={
isFocused ? 'auto' : 'no-hide-descendants'
}
style={
// Avoid positioning the focused header absolutely
// Otherwise accessibility tools don't seem to be able to find it
(mode === 'float' && !isFocused) || headerTransparent
? styles.header
: null
}
>
{header !== undefined ? header(props) : <Header {...props} />}
</View>
</NavigationRouteContext.Provider>
</NavigationContext.Provider>
);
})}
</View>
);
}