mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-04-24 04:25:34 +08:00
fix: consider header colors when managing statusbar
This commit is contained in:
@@ -5,18 +5,24 @@ import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import type { Layout } from '../types';
|
||||
|
||||
type Props = {
|
||||
dark: boolean | undefined;
|
||||
layout: Layout;
|
||||
insets: EdgeInsets;
|
||||
style: any;
|
||||
};
|
||||
|
||||
export default function ModalStatusBarManager({
|
||||
dark,
|
||||
layout,
|
||||
insets,
|
||||
style,
|
||||
}: Props) {
|
||||
const { dark } = useTheme();
|
||||
const [isDark, setIsDark] = React.useState(true);
|
||||
const { dark: darkTheme } = useTheme();
|
||||
const [overlapping, setOverlapping] = React.useState(true);
|
||||
|
||||
const enabled = layout.width && layout.height > layout.width;
|
||||
const scale = 1 - 20 / layout.width;
|
||||
const offset = (insets.top - 34) * scale;
|
||||
|
||||
const flattenedStyle = StyleSheet.flatten(style);
|
||||
const translateY = flattenedStyle?.transform?.find(
|
||||
@@ -24,28 +30,29 @@ export default function ModalStatusBarManager({
|
||||
)?.translateY;
|
||||
|
||||
React.useEffect(() => {
|
||||
const isLandscape = layout.width > layout.height;
|
||||
const scale = 1 - 20 / layout.width;
|
||||
|
||||
if (dark || isLandscape || !layout.width) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const listener = ({ value }: { value: number }) => {
|
||||
const isOverlappingStatusBar = value / scale < insets.top / 3;
|
||||
setIsDark(isOverlappingStatusBar);
|
||||
setOverlapping(value < offset);
|
||||
};
|
||||
|
||||
const sub = translateY?.addListener(listener);
|
||||
|
||||
return () => translateY?.removeListener(sub);
|
||||
}, [dark, insets.top, layout.height, layout.width, translateY]);
|
||||
}, [enabled, offset, translateY]);
|
||||
|
||||
if (dark) {
|
||||
if (!enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const darkContent = dark ?? !darkTheme;
|
||||
|
||||
return (
|
||||
<StatusBar animated barStyle={isDark ? 'dark-content' : 'light-content'} />
|
||||
<StatusBar
|
||||
animated
|
||||
barStyle={overlapping && darkContent ? 'dark-content' : 'light-content'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ type Props = ViewProps & {
|
||||
gesture: Animated.Value;
|
||||
layout: Layout;
|
||||
insets: EdgeInsets;
|
||||
headerDarkContent: boolean | undefined;
|
||||
pageOverflowEnabled: boolean;
|
||||
gestureDirection: GestureDirection;
|
||||
onOpen: () => void;
|
||||
@@ -466,6 +467,7 @@ export default class Card extends React.Component<Props> {
|
||||
gestureEnabled,
|
||||
gestureDirection,
|
||||
pageOverflowEnabled,
|
||||
headerDarkContent,
|
||||
children,
|
||||
containerStyle: customContainerStyle,
|
||||
contentStyle,
|
||||
@@ -525,15 +527,21 @@ export default class Card extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<CardAnimationContext.Provider value={animationContext}>
|
||||
{index === 0 &&
|
||||
next &&
|
||||
styleInterpolator === forModalPresentationIOS ? (
|
||||
<ModalStatusBarManager
|
||||
layout={layout}
|
||||
insets={insets}
|
||||
style={cardStyle}
|
||||
/>
|
||||
) : null}
|
||||
{
|
||||
// StatusBar messes with translucent status bar on Android
|
||||
// So we should only enable it on iOS
|
||||
Platform.OS === 'ios' &&
|
||||
index === 0 &&
|
||||
next &&
|
||||
styleInterpolator === forModalPresentationIOS ? (
|
||||
<ModalStatusBarManager
|
||||
dark={headerDarkContent}
|
||||
layout={layout}
|
||||
insets={insets}
|
||||
style={cardStyle}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
<Animated.View
|
||||
style={{
|
||||
// This is a dummy style that doesn't actually change anything visually.
|
||||
|
||||
@@ -27,6 +27,7 @@ type Props = TransitionPreset & {
|
||||
layout: Layout;
|
||||
gesture: Animated.Value;
|
||||
scene: Scene;
|
||||
headerDarkContent: boolean | undefined;
|
||||
safeAreaInsetTop: number;
|
||||
safeAreaInsetRight: number;
|
||||
safeAreaInsetBottom: number;
|
||||
@@ -91,6 +92,7 @@ function CardContainer({
|
||||
getPreviousScene,
|
||||
getFocusedRoute,
|
||||
mode,
|
||||
headerDarkContent,
|
||||
headerMode,
|
||||
headerShown,
|
||||
headerStyleInterpolator,
|
||||
@@ -248,6 +250,7 @@ function CardContainer({
|
||||
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
|
||||
pointerEvents={active ? 'box-none' : pointerEvents}
|
||||
pageOverflowEnabled={headerMode !== 'float' && mode === 'card'}
|
||||
headerDarkContent={headerDarkContent}
|
||||
containerStyle={hasAbsoluteHeader ? { marginTop: headerHeight } : null}
|
||||
contentStyle={[{ backgroundColor: colors.background }, cardStyle]}
|
||||
style={[
|
||||
|
||||
@@ -39,6 +39,7 @@ import type {
|
||||
StackDescriptor,
|
||||
Scene,
|
||||
} from '../../types';
|
||||
import Color from 'color';
|
||||
|
||||
type GestureValues = {
|
||||
[key: string]: Animated.Value;
|
||||
@@ -529,6 +530,8 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
const {
|
||||
headerShown = true,
|
||||
headerTransparent,
|
||||
headerStyle,
|
||||
headerTintColor,
|
||||
cardShadowEnabled,
|
||||
cardOverlayEnabled = Platform.OS !== 'ios' || mode === 'modal',
|
||||
cardOverlay,
|
||||
@@ -592,6 +595,19 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
const headerHeight =
|
||||
headerShown !== false ? headerHeights[route.key] : 0;
|
||||
|
||||
const { backgroundColor: headerBackgroundColor } =
|
||||
StyleSheet.flatten(headerStyle) || {};
|
||||
|
||||
let headerDarkContent: boolean | undefined;
|
||||
|
||||
if (headerShown) {
|
||||
if (headerTintColor) {
|
||||
headerDarkContent = Color(headerTintColor).isDark();
|
||||
} else if (typeof headerBackgroundColor === 'string') {
|
||||
headerDarkContent = !Color(headerBackgroundColor).isDark();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<MaybeScreen
|
||||
key={route.key}
|
||||
@@ -631,6 +647,7 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
mode={mode}
|
||||
headerMode={headerMode}
|
||||
headerShown={headerShown}
|
||||
headerDarkContent={headerDarkContent}
|
||||
hasAbsoluteHeader={
|
||||
isFloatHeaderAbsolute && !headerTransparent
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user