White background flashing when switching screens - React-Navigation v5 - react-native

I'm migrating a RN project version 4 to 5.
When switching screens there was an issue with a white background flashing in.
In v4 this was solved by setting cardStyle: { backgroundColor: material.containerBgColor } in the StackNavigation options.
However in v5 I'm unable to fix it with the same approach:
<Stack.Navigator cardStyle={{ backgroundColor: material.containerBgColor }} ...>
White flash has come back. Any idea how to fix it? Thanks.
Update:
The structure of the navigation may be important:
const AppTabNavigator = () => (
<Tab.Navigator>
<Tab.Screen name="Home" component={Home} />
<Stack.Screen name="ScreenD" component={ScreenD} />
<Stack.Screen name="ScreenE" component={ScreenE} />
<Stack.Screen name="ScreenF" component={ScreenF} />
</Tab.Navigator>
)
...
<Stack.Navigator
...
cardStyle={{ backgroundColor: material.containerBgColor }}
>
<Stack.Screen name="Home" component={AppTabNavigator} />
<Stack.Screen name="ScreenA" component={ScreenA} />
<Stack.Screen name="ScreenB" component={ScreenB} />
<Stack.Screen name="ScreenC" component={ScreenC} />
</Stack.Navigator>
Going from ScreenD to ScreenE does the flashing issue. I'm not sure about the other screens as they don't make any network request / async stuff.

I faced the same issue and dived into an investigation. It seems that the detachment of the screens causes it. I found a few approaches. You can choose one according to your needs. They are the following:
You can specify a view wrapper of the navigator with the same background color as the screens one like:
<View style={{ flex: 1, backgroundColor: '#YOUR_SCREEN_COLOR' }}>
// It could be your NavigationContainer or your StackNavigator depends on your goals
<Navigator />
</View>
You can also specify your screen mode to be modal in the stack view config this prevents the screens from being detached like:
<StackNavigator.Navigator mode="modal">
{/*.... Your stack screens ... */}
</StackNavigator.Navigator>
You can add your custom overlay in screenOptions by using the cardOverlay prop:
cardOverlay: () => (
<View
style={{
flex: 1,
backgroundColor: '#YOUR_COLOR',
}}
/>)
Reference: https://reactnavigation.org/docs/stack-navigator/#cardoverlay
You can use the cardStyleInterpolator:
This allows you to customize the animation transitions when navigating from screen to screen.
Here are the snippets from the original documentation:
const forFade = ({ current, closing }) => ({
cardStyle: {
opacity: current.progress,
},
});
<Stack.Screen
name="Profile"
component={Profile}
options={{ cardStyleInterpolator: forFade }}
/>
Stack Navigator exposes various options to configure the transition animation when a screen is added or removed.
Reference: https://reactnavigation.org/docs/stack-navigator/#animation-related-options

Fixed it by using the DarkTheme for the Navigation Container
import { NavigationContainer, DarkTheme } from '#react-navigation/native';
return (
<NavigationContainer theme={DarkTheme}>
{children}
</NavigationContainer>

I am also using StackNavigator in v5, but none of the answers worked for me. That's how I solved the issue:
const navigatorOptions = {
headerShown: false,
cardStyle: { backgroundColor: 'transparent' },
cardStyleInterpolator: ({ current: { progress } }) => ({
cardStyle: {
opacity: progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
}),
},
overlayStyle: {
opacity: progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 0.5],
extrapolate: 'clamp',
}),
},
}),
}
...
<AppStack.Navigator
screenOptions={navigatorOptions}
mode="modal"
>
...
I've found the solution here: https://reactnavigation.org/docs/stack-navigator#transparent-modals

An easy fix to this problem that worked for me is to set the sceneContainerStyle in the Tab Navigator like this:
<Tab.Navigator sceneContainerStyle={{backgroundColor: 'black'}}>
...//your screens here
</Tab.Navigator>

const App = () => (
<View style={styles.appStyle}>
<Navigation />
</View>
);
const styles = StyleSheet.create({
appStyle: { flex: 1, backgroundColor: "#000" }
});

Using react-native-navigation:^6.0.0, I tested everything here but actually the only things that work is to set a theme on the NavigationContainer
theme={{
colors: {
background: 'black',
},
}}

I used the solution provided by #jul
<SafeAreaProvider style={{ backgroundColor: "black" }}>
Small difference is that our SafeAreaProvider wraps NavigationContainer at a much higher level.
<SafeAreaProvider style={{ backgroundColor: "black" }}>
...
<NavigationContainer />
...
</SafeAreaProvider>
Serves as a default background color without using native modules to set the root background color

In my case it was due to the default styles of a SafeAreaProvider inside a NavigationContainer. Setting
<SafeAreaProvider style={{ backgroundColor: "black" }}>
did the trick.

I have solved the issue by disabling the sceneAnimationEnabled of the Tab Navigator.
<Tab.Navigator
sceneAnimationEnabled={false}>
{...}
</Tab.Navigator>

I solved this by setting lazy={false} in the <Tabs.Navigator> component:
<Tabs.Navigator
lazy={false}
>

cardStyle is an option on the screen, not the navigator.
<Stack.Navigator screenOptions={{ cardStyle: backgroundColor: material.containerBgColor }}>
{/* ... */}
</Stack.Navigator>
Or
<Stack.Navigator>
<Stack.Screen
name="Home"
component={AppTabNavigator}
options={{ cardStyle: backgroundColor: material.containerBgColor }}
/>
{/* ... */}
</Stack.Navigator>
Reference: https://reactnavigation.org/docs/en/next/stack-navigator.html#cardstyle
Probably a better way is to use the theming system to pass your colors rather than specifying it for every navigator: https://reactnavigation.org/docs/en/next/themes.html

The problem with me was that I had enableScreens() was enabled in my App.tsx.
You have 2 options:
Remove the enableScreens() call
add detachPreviousScreen: false to your screenOptions of your navigator and enableScreens(true) to your App.tsx

for expo you can change backgroundColor in App.json file to #000000.
The background color for your app, behind any of your React views. This is also known as the root view background color.

Put the outside your NavigationContainer with flex: 1 and backgroundColor with the color you like

I solved this problem doing this in the app.json file: (but i had the same background color on every page)
"expo": {
"backgroundColor": "#YOUR_COLOR",
}

I solved this problem with this code: you can also put cardStyleInterpolator in the options property of each <Screen>
const notAnimation = () => ({});
screenOptions={{ cardStyleInterpolator: notAnimation }}>

Using react-native-navigation:^4.4.4, the issue is resolved by disabling the animation.
const AppNavigator = createStackNavigator(
{...<YOUR_CONFIG>},
{
...
defaultNavigationOptions: {animationEnabled: false}, // Add this line
},
);
No need to worry about backgroundColor 👍

Try this , it will work -
Just wrap the navigator inside a view and give it a background color and don't forget to set flex

For me adding cardStyle was enough to fix it.
const navigatorOptions = {
...
cardStyle: { backgroundColor: 'transparent' },
...
}

Setting the background color merely "hides" the problem. For me, the problem arose when I switched to react-native-navigation:^6.0.0, but it only happened on screens where I was using LayoutAnimation.configureNext() in a useEffect callback (used to trigger effects when a state changes).
What completely solved the problem for me was to put the LayoutAnimation.configureNext() in the callback function that triggers the state change, instead of passively listening to it with useEffect (because it seemed that the useEffect trigger on render + the LayoutAnimation caused the flash).

I have solved this issue by wrapping navigationContainer inside the SafeAreaProvider which is imported from react-native-safe-area-context and give background color to it.
<SafeAreaProvider style={{backgroundColor: Colors?.backgroundColor}}>
<NavigationContainer>
<StackNavigator />
</NavigationContainer>
</SafeAreaProvider>
try it if this works fine, then give upvote and tick the answer.
Thanks

Related

React navigation V6 flashes white on slide between screen

I am using react navigation V6. And I have 6 components. But when I navigate from the first to the sixth: the transition goes through all the active components and flashes white. I would like to either remove the white flash or change the effect of the slide transition to fade. Thank you.
<View style={{flex: 1, borderTopWidth: 1, borderTopColor: '#5656582b'}}>
<Tab.Navigator
initialRouteName="Tous"
screenOptions={{
tabBarScrollEnabled: true,
tabBarIndicatorStyle: {
backgroundColor: '#60103b',
height: 4,
},
tabBarActiveTintColor: '#60103b',
tabBarInactiveTintColor: 'black',
tabBarLabelStyle: { fontSize: 17,color: 'black',fontWeight: "500",letterSpacing: 1},
tabBarItemStyle: {width: 'auto', padding: 10},
lazy:true,
swipeEnabled:false,
}
}
sceneAnimationEnabled = {false}
//animationEnabled: false}
>
<Tab.Screen name="Tous" initial component={Home} />
<Tab.Screen name="Livres" component={LivreHome} />
<Tab.Screen name="Livres Audios" component={LivreAudio} />
<Tab.Screen name="Podcasts" component={PodcastHome} />
<Tab.Screen name="Magasines" component={MagasineHome} />
<Tab.Screen name="Documents" component={DocumentHome} />
</Tab.Navigator>
</View>
First of all wrap <Tab.Navigator> in NavigationContainer
For remove white flash there is a way by giving dark theme to NavigationContainer
Example code
import {
NavigationContainer,
DarkTheme,
} from '#react-navigation/native';
export default () => {
return (
<NavigationContainer theme={DarkTheme}>
{/*Other Tab.Navigtor content*/}
</NavigationContainer>
);
};
<NavigationContainer theme={DarkTheme} >
{userDataSelect.hasOwnProperty('access_token')?
<MainNavigation /> : <AuthNavigation />
}
</NavigationContainer>
When I navigate on 2 successive screens, for example 1 to 2 or 2 to 3, there is no flash. But when I navigate from 1 to 5 I always have the white flash as if the navigation goes through all the components of the browser

How to set borderRadius (rounded corners) in React Native Header Bar?

I'm using Native Stack Navigator v6 and trying to add borderBottomRightRadius and borderBottomLeftRadius as shown below. It's working in Expo Web but not in iOS or Android, as shown in screenshot below.
I'd appreciate guidance on how to fix this, or if this is not the right approach, please suggest another way to achieve bottom rounded corners for the header bar.
<HomeStack.Screen
name="HomeScreen"
component={HomeScreen}
options= {{
headerTitle: "Home Screen",
headerStyle: {
backgroundColor: '#21ABA5',
borderBottomRightRadius: 20,
borderBottomLeftRadius: 20,
overflow: 'hidden',
background: 'transparent'
},
headerTitleStyle: {
color: '#fff'
},
headerTintColor: 'white',
headerTransparent: true
}}
/>
Let me edit my answer. If you don't want for web at all, you can create your own header. If you want to apply to all screens, add it to Stack.Navigator's ScreenOptions.
import { getHeaderTitle } from "#react-navigation/elements";
function StackScreen() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
header: ({ navigation, route, options, back }) => {
const title = getHeaderTitle(options, route.name);
return (
<MyHeader
title={title}
leftButton={
back ? (
<MyBackButton onPress={navigation.goBack} />
) : undefined
}
style={options.headerStyle}
/>
);
},
}}
/>
</Stack.Navigator>
);
}
After further research turns out this is impossible.
Native Stack Navigator depends on native platform options which apparently don't support borderRadius out-of-the-box. The only option that can be affected via the headerStyle is backgroundColor.
Other options include using the Stack Navigator instead of the Native Stack Navigator, or building an entirely custom header component. However, the latter loses most of built-in the advantages of the Native Stack Navigator. Thus, I'll be switching to the JS-based Stack Navigator which is far more customizable.

How to use modalPresentationStyle .fullscreen in React Native navigation

I am trying to create my first React Native application. I have a login screen from which I want to navigate to a register screen if users want to sign up.
To achieve this, I was thinking of opening a modal above the first screen (login). I created the following:
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
const Stack = createStackNavigator();
function RegisterScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 30 }}>Register Screen</Text>
<Button onPress={() => navigation.goBack()} title="Go back" />
</View>
);
}
function MyStack() {
return (
<Stack.Navigator>
<Stack.Group>
<Stack.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
</Stack.Group>
<Stack.Group screenOptions={{ presentation: "modal", headerShown: false }} options={{modalPresentationStyle: 'fullScreen'}}>
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Group>
</Stack.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<MyStack />
</NavigationContainer>
);
}
This works perfectly fine, the modal shows. However, as you can see in this screenshot below, the modal is shown with modalPresentationStyle .automatic (in iOS). I want it to show as .fullScreen, so the hierarchy is not visible in the screen. Basically, I want the view to be shown from the safeArea/statusBar all the way down to the safeArea on the bottom.
How can I achieve this?
The terminology in React Native isn't the same as terminology in native apps. If you want a full-screen screen, you probably don't want a modal. If you want a vertical animation, change the animation based on docs:
screenOptions={{ cardStyleInterpolator: CardStyleInterpolators. forVerticalIOS, headerShown: false }}
https://reactnavigation.org/docs/stack-navigator/#animation-related-options
There's also no modalPresentationStyle: 'fullScreen' in documentation and Group component doesn't take an options prop.
The accepted answer states:
If you want a full-screen screen, you probably don't want a modal.
That's not necessarily true. Use
screenOptions={{ presentation: 'transparentModal' }}
or
screenOptions={{ presentation: 'fullScreenModal' }}
for a full screen modal animation you can't dismiss on iOS.

TouchableOpacity inside header is affected by view with absolute position in the screen on android

I have a Viewinside a SafeAreaView in a screen which has postion:absolute. When I include the view, a TouchableOpacity rendered in the header no longer responds in ANDROID (but always works fine in iOS).
Home screen:
render() {
return (
<SafeAreaView style={styles.SafeAreaContainer}>
<View style={styles.homeBackgroundContainer}> {/* <--- offending view! */}
<BlueCircle style={styles.homeBackgroundSvg} />
</View>
<View style={styles.homeView}>
<View style={styles.homeGreetingContainer}>
<Title style={styles.homeGreeting}>Hi, </Title>
<Title style={styles.homeGreetingName}>{username}</Title>
</View>
// Other components
</View>
</SafeAreaView>
);
}
styles.homeBackgroundContainer:
homeBackgroundContainer: {
borderColor: COLOR.transparent,
position: 'absolute',
top: '-50%',
left: '-25%',
right: 0,
bottom: 0,
},
The header is declared in the drawer navigator screen options. Note the TouchableOpacity in the header - this is used to open the drawer.
works in iOS
works from the "Content" screen
works when the homeBackgroundContainer(and its children) are commented out (Home Screen)
DOES NOT WORK IN ANDROID when homeBackgroundContainer is rendered (Home Screen).
Drawer Navigation & Header:
function DrawerContainer() {
return (
<Drawer.Navigator
screenOptions={({navigation}) => ({
headerLeft: titleLogo,
headerStyle: {
// colors only
},
headerTitle: () => {},
headerRight: () => (
<TouchableOpacity
style={styles.hamburgerIconButton}
onPress={() => navigation.toggleDrawer()}>
<Image source={require('../assets/hamburger.png')} />
</TouchableOpacity>
),
})}>
<Drawer.Screen
name="Home"
component={HomeScreen}
options={{
headerShown: true,
// more options
}}
/>
<Drawer.Screen
name="Content"
component={ContentScreen}
options={{
headerShown: true,
}}
/>
</Drawer.Navigator>
);
}
styles.hamburgerIconButton:
hamburgerIconButton: {
marginRight: 20,
},
Other questions on SO have shown that absolute positioning can affect TouchableOpacity. I have tried setting the zIndex of the TouchableOpacity to 1, and also tried setting a positive elevation value but there is no change in behaviour.
Visually, I can see the header colors and content is rendered on top of the view which is the desired state. So if the header is rendered over the view, why isn't the TouchableOpacity responding?
To get the TouchableOpacityin a navigation header to respond on android when there's a view on the screen which has postion: absolute, I had to bring the headerStyle(not the touchable opacity style) forward on the z-axis.
screenOptions={() => ({
headerStyle: {
zIndex: 1,
},
})};

React Native Tab Bar white space under tabs

I am using a tab bar navigator with SafeAreaView.
If I comment out the tab bar nav the parent view covers the entire screen. However when I add a Tab bar it shows a small white view under the tab bar section.
const App = () => {
return (
<SafeAreaView style={styles.droidSafeArea}>
<View style={{ backgroundColor: "red", flex: 1 }}>
<TabNavigator key="MainTabNav" />
</View>
</SafeAreaView>
);
};
export default App;
const styles = StyleSheet.create({
droidSafeArea: {
flex: 1,
backgroundColor: "#2F3438",
}
});
Try this
screenOptions={{
tabBarStyle: {
paddingBottom:0,
},
}}
Please use the tab bar outside the safeAreaView else the safe area view will calculate the notch and will render the tab bar above the notch.
2.If you are using tab bar inside the safe area view use the force inset property of safe area view : <SafeAreaView forceInset = {bottom : 'never} this will make the safeareaview collide with bottom area and your tab bar will render properly.
Note : by using this method you would have to be a bit accurate in providing the styles.
const App = () => {
return (
<SafeAreaView style={styles.droidSafeArea} forceInset = {bottom : 'never'}>
<View style={{ backgroundColor: "red", flex: 1 }}>
<TabNavigator key="MainTabNav" />
</View>
</SafeAreaView>
);
};
export default App;
const styles = StyleSheet.create({
droidSafeArea: {
flex: 1,
backgroundColor: "#2F3438",
}
});
I had the exact same issue and what I did is not use SafeAreaView at all around the tab bar, but simply assigning the color I want the white space to have as the background color for the tab bar.
In your example that would be:
return (
<View>
<TabNavigator style={{ backgroundColor: "#2F3438" }} key="MainTabNav" />
</View>
);
<NavigationContainer>
<Tab.Navigator
tabBarOptions={{
activeTintColor: Colors.tabIconSelected,
inactiveTintColor: Colors.tabIconDefault,
style: styles.container
}}/>
</NavigationContainer>
const styles = StyleSheet.create({
container: {
backgroundColor: Colors.darkBackgroundColor,
borderTopWidth: 0
}
});
Note : borderTopWidth: 0 worked for me
For react native navigation V5
<Tab.Navigator
tabBarOptions={{
style: {
borderTopWidth: 0
}
}}
>
<Tab.Screen/>
<Tab.Navigator>
Note: this is for bottom tab
When I was implementing floating button on bottomTabNavigation followed this post, I faced similar issue that tabBar has dirty white space with shadow(I used shadow in style of component).
I used React navigation v6.
issue image1, issue image2 (Sorry, It's my first Answer I post, I can't embed image yet)
I tried to remove it with borderWidth: 0, but not worked.
My case, below is worked for me.
Try this
borderRadius: 25 // some much number that near tabbar height
on
<Tab.Navigator
tabBar={(props) => (
<View style={styles.navigatorContainer}>
<BottomTabBar {...props} />
{isIphoneX() && (
<View
style={[
styles.xFillLine,
{ backgroundColor: "#fff" },
]}
/>
)}
</View>
)}
screenOptions={{
headerShown: false,
tabBarShowLabel: false,
tabBarStyle: {
borderRadius: 25, // add here
borderTopWidth: 0,
borderRadius: 25,
backgroundColor: "transparent",
elevation: 30,
},
tabBarItemStyle: { backgroundColor: "#fff" },
}}
>
...
Then result image is this.
I don't understand why It was worked, but I hope it works for someone.
I had this issue when i was using the TabBarIcon property within the Tab.Screen
Tab being const Tab = createBottomTabNavigator()
I couldn't solve the issue no matter how i used the SafeAreaView.
I solved it by not using the TabBarIcon property and instead making a custom component for the tabBar property on the higher level Tab.Navigator as outlined in the react native docs https://reactnavigation.org/docs/bottom-tab-navigator/
When i created the custom tabBar component it all worked as expected, no funky use of SafeAreaView.