I just started developing an app in react-native and added a bottom navigation. I then started styling the components and noticed a white line above the navigation, which i simply can't get rid of.
Picture of the Problem
Any idea on how to make that line have the same color as the background would be appreciated. It might be possible that the default background color behind the views is "shining through" since it's white and I have no idea how to change it. The app is only supposed to work on my own iPhone XR, so I'm not concerned about compatibility with android or other iPhone models
I'm a complete beginner regarding react-native, so bear with me here.
Here is my code so far:
Navigation
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
tabBarOptions={{
activeTintColor: Colors.tabIconSelected,
inactiveTintColor: Colors.tabIconDefault,
style: styles.container
}}>
<Tab.Screen
name="Payments"
component={PaymentScreen}
options={{
tabBarIcon: ({focused}) => <TabBarIcon focused={focused} name="logout"/>
}}/>
<Tab.Screen
name="Income"
component={IncomeScreen}
options={{
tabBarIcon: ({focused}) => <TabBarIcon focused={focused} name="login"/>
}}/>
</Tab.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: Colors.darkBackgroundColor,
}
});
Payment View
export default class PaymentScreen extends Component{
render() {
return (
<View style={styles.container}>
<Text style={styles.text}>Payments!</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container:{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: Colors.backgroundColor,
},
text:{
color: Colors.textColor
}
});
Edit: the API has changed since I posted this answer. See the solution below if this one doesn't work for you
Figured it out myself after a bit of trial and error. The Tab.Navigator class of the NavigationContainer has a prop called tabBarOptions which takes a stylesheet as its style option. And of course the border of the component can be changed here as well.
Here's the catch though: setting borderWidth to 0 does not hide the white border above the navigation. Only setting borderTopWidth to 0 has the desired effect.
So the full solution looks like this:
<NavigationContainer>
<Tab.Navigator
tabBarOptions={{
activeTintColor: Colors.tabIconSelected,
inactiveTintColor: Colors.tabIconDefault,
style: styles.container
}}/>
</NavigationContainer>
const styles = StyleSheet.create({
container: {
backgroundColor: Colors.darkBackgroundColor,
borderTopWidth: 0
}
});
You can remove it in tabBarStyle in screenOption props likes below
<Tab.Navigator
screenOptions={
tabBarStyle:{borderTopWidth:0}
}
/>
Using React Navigation 6.x
In my case, I had to put the tabBarStyle on every Tab.Screen so for example:
<Tab.Screen name="HomeTab" component={xx} options={({ route }) => ({
title: 'Home',
tabBarStyle: {borderTop: 0},
})} />
<Tab.Screen name="SecondTab" component={xx} options={({ route }) => ({
title: 'SecondTab',
tabBarStyle: {borderTop: 0},
})} />
<Tab.Screen name="ThirdTab" component={xx} options={({ route }) => ({
title: 'ThirdTab',
tabBarStyle: {borderTop: 0},
})} />
(unable to comment this answer as link is too much lengthy)
This line cannot be removed it seems. It'a coming by default when BottomTab is rendered to differentiate BottomTab and rest of the screen. Go through this example and try to apply multiple background colors for `BottomTab' then you will understand a bit clearly.
in Bottom Tabs Navigator v6,
you can use tabBarStyle prop to resolve this white line issue.
make sure to gave the borderTopWidth:0,
screenOptions={({route}) =>({
tabBarStyle: {
height: IS_IPHONE ? RFValue(47) : RFValue(42),
backgroundColor: AppTheme.colors.darkGrey,
borderTopWidth: 0,
},
}
)
}
Related
I am using the default stack from react navigation, and in my navigator screen options I have my own custom header header: (props) => <Header props={props} />. In my screen, I have a background colour, and in my SafeAreaView I have a background colour. For illustration I have set them both to the same.
In my custom header I am applying margin of 15 horizontally as well as to the top. So it is the colour underneath the custom header.
My question is this: Where is this header background colour being set? (the light grey one)
Here you can see what I mean
I have tried headerStyle {backgroundColor}, but no difference (i believe because using header overrides a lot of these options? Unsure though as it is not stated in the documentation.)
For reference, I am basing it off of this documentation: https://reactnavigation.org/docs/native-stack-navigator/#options
However it appears that this background colour is being set somewhere else entirely? And so I'm not sure where.
I realize setting headerTransparent and applying margin for each screen is a viable solution but I figure there is a better way to simply set the background colour of this space correctly.
edit with header component:
export default function Header({ props }) {
const { navigation, route, options, back } = props;
const title = getHeaderTitle(options, route.name);
return (
<View style={styles.header}>
<View style={styles.container}>
{back && (
<IconButton
type="ion"
name="chevron-back"
size={26}
iconStyle={styles.backButton}
onPress={navigation.goBack}
/>
)}
<Text style={styles.title}>{title}</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
width: screenWidth - 30,
height: 60,
borderRadius: 10,
padding: 15,
marginTop: 15,
backgroundColor: colors.white,
alignSelf: "center",
flexDirection: "row",
alignItems: "center",
},
backButton: {
zIndex: 1,
},
title: {
flex: 1,
textAlign: "center",
flexDirection: "column",
fontSize: 24,
fontWeight: "bold",
// marginLeft: -26,
},
header: {
backgroundColor: colors.neutral95,
},
});
You find a minimal repo here and a minimal snack available here. They both show the issue nicely with very minimal code.
I found lots of questions online about how to use KeyboardAvoidingViev with react navigation e.g.
The popular proposed solution is
keyboardVerticalOffset={headerHeight + 64}
That does not work completely for me. The 64 seems arbitrary and somehow on iOS the view shrinks after closing the keyboard.
Here is what I know so far:
It is definitely react navigation (headers and/or Material Bottom Tabs Navigator) and KeyboardAvoidingViev working together. If you remove the Material Bottom Tabs Navigator, it works.
Solutions including SafeAreaView did make it worse
Solutions with behavior={"position"} did make it worse
Solutions with +64 are not working
I found out that the solution of David Scholz works nicely, if I remove the Material Bottom Tabs Navigator.
Everything works fine on android (Samsung Galaxy 7S and emulator). From what I know, I would recommend to avoid KeyboardAvoidingViev with react navigation on android. So in fact, KeyboardAvoidingViev is also not working fully for android, but you don't need to use it.
Any help is appreciated. Thank you!
I create a wrapping component like this and pass the offset from the screen so that it gets the current context.
import React from 'react'
import { ViewStyle } from 'react-native'
import { KeyboardAvoidingView, Platform, ScrollView } from 'react-native'
type Props = {
children: any
keyboardVerticalOffset?: number
contentContainerStyle?: ViewStyle
}
export default function KeyboardWrapper({
children,
keyboardVerticalOffset,
...rest
}: Props): JSX.Element {
return (
<KeyboardAvoidingView
style={{ flex: 1 }}
keyboardVerticalOffset={keyboardVerticalOffset}
{...(Platform.OS === 'ios' ? { behavior: 'padding' } : {})}>
<ScrollView
bounces={false}
showsVerticalScrollIndicator={false}
{...rest}>
{children}
</ScrollView>
</KeyboardAvoidingView>
)
}
Usage in the screen
If you have any sticky elements like topbar or bottom bar you need to add the height so that the keyboard adds the offset correctly.
import { useHeaderHeight } from '#react-navigation/elements'
const height = useHeaderHeight()
<KeyboardWrapper
keyboardVerticalOffset={height}
contentContainerStyle={{ flex: 1 }}>
// ... your elements
</KeyboardWrapper>
This is a working demo https://snack.expo.dev/#raajnadar/fix-keyboardavoidingview-not-working
There is no need to add 64 to the headerHeight. Here is how I would solve this problem.
const flatListRef = createRef()
const [data, setData] = useState()
const headerHeight = useHeaderHeight();
<View style={{ flex: 1 }}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={headerHeight}
style={{ flex: 1 }}>
<FlatList
data={data}
ref={flatListRef}
onContentSizeChange={() =>
flatListRef.current.scrollToEnd({ animated: true })
}
onLayout={() => flatListRef.current.scrollToEnd({ animated: true })}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<Text style={{ flex: 1, paddingVertical: 20 }}>{item.id}</Text>
)}
/>
<View style={{ flex: 1, marginBottom: 40 }}>
<TextInput
style={{
backgroundColor: '#2E2E2E',
width: '100%',
borderRadius: 18,
height: 36,
paddingLeft: 10,
paddingRight: 10,
color: '#FFFFFF',
}}
/>
</View>
</KeyboardAvoidingView>
</View>
Here is a quick and dirty snack which contains an header from a StackNavigator.
I am trying to achieve a dismissible bar for my modal. Something like in this image:
What i am at right now
code:
<RootStack.Group
screenOptions={{
presentation: "modal",
gestureEnabled: true,
headerBackTitleVisible: false,
headerTitle: "",
hideNavigationBar: false,
gestureEnabled: true,
}}
>
<RootStack.Screen name="MyModal" component={ModalScreen} />
</RootStack.Group>
I am not sure if there is a built-in solution to that, but for sure you can hide the status bar per screen. In this case that's MyModal screen. And once you hide it you can implement a custom header that can be anything you wish it to be, but you will need to hook back actions etc.
Another way is to implement a custom Header, React Navigation has an API for that https://reactnavigation.org/docs/stack-navigator#header
Since there isn't a built in solution, you could achieve what you want by doing something like so:
(1) You will need to replace react-navigation's generated header with a custom one, like the following:
function LogoTitle() {
return (
<Image
style={{ width: 50, height: 50 }}
source={require('#expo/snack-static/react-native-logo.png')}
/>
);
}
function StackScreen() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ headerTitle: (props) => <LogoTitle {...props} /> }}
/>
</Stack.Navigator>
);
}
Source: https://reactnavigation.org/docs/headers#replacing-the-title-with-a-custom-component
(2) Your custom component would have to look something like this:
export default function App() {
const cancelButtonOnPress = () => {
navigation.goBack();
}
return (
<View style={styles.container}>
<View style={styles.bar}></View>
<Text onPress={()=>{cancelButtonOnPress()}} style={styles.cancelButton}>Cancel</Text>
</View>
);
}
const styles = StyleSheet.create({
cancelButton: {
color: '#0573ad'
},
bar: {
alignSelf: 'center',
width: 40,
height: 7,
backgroundColor: 'gray',
borderRadius: 40,
},
container: {
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
padding: 8,
borderWidth: 1,
borderColor: 'black',
},
});
The above App looks like the following:
(3) Since you are passing your custom component to react-navigation, the gesture swipe to dismiss the modal should be retained, if for some reason it isn't, you could try setting fullScreenGestureEnabled to true in screenOptions.
The value headerTintColor for the back button is being ignored in iOS 15 and instead uses the system standard blue color. The Title and Header Right buttons still respect the value that I pass in. I am using react-navigation v5 and I am trying to avoid upgrading to v6 right now, if I can avoid it. (And I don't know if this is happening in v6 or not?). This occurs on both the device and the simulator.
iOS 15 (via Simulator):
screenshot from iOS 15 showing blue-colored back button
iOS <= 14 (via Simulator):
screenshot from iOS 13 showing white colored back button
<Stack.Navigator
initialRouteName="EventList"
screenOptions={{gestureEnabled: true}}>
<Stack.Screen
name="EventListScreen"
component={EventTabs}
options={({navigation}) => ({
title: parametersBand.APP_NAME,
headerTintColor: colors.NAV_TEXT_COLOR,
headerStyle: styles.navBar,
headerTitleStyle: styles.navBarTextStyle,
headerTitleAllowFontScaling: false,
headerRight: () => (
<HeaderRightContainer>
<HeaderRightButton
onPress={() =>
analytics()
.logEvent('extras')
.then(() => navigation.navigate('Extras'))
}
hitSlop={hitSlop}>
<FontAwesome name="bars" style={styles.navBarButtonHamburger} />
</HeaderRightButton>
</HeaderRightContainer>
),
headerBackAllowFontScaling: false,
headerBackTitle: null,
headerBackTitleVisible: false,
backgroundColor: colors.PRIMARY,
borderBottomWidth: 0,
})}
/>
<Stack.Screen
name="EventDetailScreen"
component={EventDetail}
options={({route, navigation}) => ({
title: route.params.event.title,
headerTintColor: colors.NAV_TEXT_COLOR,
headerStyle: styles.navBar,
headerTitleStyle: styles.navBarTextStyle,
headerTitleAllowFontScaling: false,
headerRight: () => (
<HeaderRightContainer>
<HeaderRightButton
onPress={() =>
analytics()
.logEvent('setlist', {
event: `${route.params.event.YYYYMMDD} ${route.params.event.altName}`,
})
.then(() =>
showBrowser(
navigation,
route.params.event.setlistURL,
parametersBand.SETLIST_TITLE,
),
)
}
hitSlop={hitSlop}>
<HeaderRightButtonText
style={styles.navBarButtonRight}
allowFontScaling={false}>
Setlist
</HeaderRightButtonText>
</HeaderRightButton>
</HeaderRightContainer>
),
headerBackAllowFontScaling: false,
headerBackTitle: null,
headerBackTitleVisible: false,
backgroundColor: colors.PRIMARY,
borderBottomWidth: 0,
})}
/>
</Stack.Navigator>
Has anyone else encountered and fixed this yet?
I'm sure you fixed this by now but just in case anyone else runs into this, as I just did.
The tint color for the back button is handled by the theme so you can override theme.colors.primary to change the tint color for the button and title text.
<NavigationContainer theme={{
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: 'custom color here'
}}
>
{...}
</NavigationContainer>
I can't get this simple NavigatorIOS test to work. The console log in My View triggeres, and I can get it to render if I skip the NavigatorIOS component, and render MyView directly. However, when MyView is triggered from a component within the NavigatorIOS component, it won't render anything else than 'My NavigatorIOS test'.
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
NavigatorIOS,
Text,
View,
} = React;
var navigation = React.createClass ({
render: function() {
return (
<NavigatorIOS
initialRoute={{
component: MyView,
title: 'My NavigatorIOS test',
passProps: { myProp: 'foo' },
}}/>
);
},
});
var MyView = React.createClass({
render: function(){
console.log('My View render triggered');
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Hello there, welcome to My View
</Text>
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
}
});
AppRegistry.registerComponent('navigation', () => navigation);
I had a similar problem. I added the following to my Stylesheet:
...
wrapper: {
flex: 1,
}...
and then gave the NavigatorIOS component the wrapper style. That fixed the issue.
Add the container style to NavigatorIOS, it needs to be flex:1 to show the child component properly ( I had the same issue).
I ran into the same issue, my mistake was in the styles :
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
I had to remove justifyContent and alignItems from there. Problem solved for me.
I had the same issue. Turned out I had to add some margin to the top of the view inside the MyView component.
Try this:
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
NavigatorIOS,
Text,
View,
} = React;
var navigation = React.createClass ({
render: function() {
return (
<NavigatorIOS
style={styles.container}
initialRoute={{
component: MyView,
title: 'My NavigatorIOS test',
passProps: { myProp: 'foo' },
}}/>
);
},
});
var MyView = React.createClass({
render: function(){
console.log('My View render triggered');
return (
<View style={styles.wrapper}>
<Text style={styles.welcome}>
Hello there, welcome to My View
</Text>
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
},
wrapper: {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
marginTop: 80
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
}
});
AppRegistry.registerComponent('navigation', () => navigation);
I meet the same problem. For me, that is the style problem. I create and use a new style property containerNV for NavigatorIOS. That solved my problem.
var styles = StyleSheet.create({
containerNV: {
flex: 1,
backgroundColor: '#05FCFF',
}
});
I had a similar issue too (stupidly) as I'd given the NavigatorIOS a background colour, which meant it covered up the actual component inside it for some reason.
I had a similar issue when I use Navigator , and nothing help of up answers.
So I use this.forceUpdate(); to force Update in button press function
Had the same issue. Nothing was rendering inside my main screen. After 2 hours I decided to remove the <View> element I was wrapping my navigator with, and suddenly everything worked.
In a nutshell, I went from this:
<View>
<NavigatorIOS
style={styles.container}
initialRoute={{
component: MainPage,
title: 'MainPage',
}}/>
</View>
to this
<NavigatorIOS
style={styles.container}
initialRoute={{
component: MainPage,
title: 'MainPage',
}}/>