React navigation bar header has a margin on the left - reactjs

I am using React-Navigation and StackNavigator to implement a navigation bar at the top of my react native app, which shows the app logo centered and a menu button on the right. I cannot get it to take up the complete space of the header container though, there is always a margin of ~20px on the left.
As you can see in my code, I have already applied all kinds of style properties such as margin: 0, padding: 0, alignment: fill and width:100% to headerStyle, headerContainerStyle and the navigation bar component itself, but none of them helped.
App.js:
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import {Home} from "components/Home";
import {NavigationBar} from "components/NavigationBar";
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer headerStyle={styles.container}>
<Stack.Navigator
initialRouteName="Home" headerStyle={styles.container}
screenOptions={{
headerTitleContainerStyle: styles.container,
headerTitleStyle: styles.title,
headerStyle: styles.header,
headerTitle: props => <NavigationBar {...props} />
}}>
<Stack.Screen name="Home" component={Home} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignment: 'fill',
width: '100%',
height:'100%',
// useless
margin: 0
},
title: {
},
header: {
flex: 1,
justifyContent: 'center',
backgroundColor: 'red',
// useless
padding: 0,
margin: 0,
marginLeft: 0,
alignment: 'fill',
alignItems: 'center',
alignContent: 'center'
}
});
NavigationBar.jsx:
import React from 'react';
import { Button, StyleSheet, View } from 'react-native';
import { CenteredView } from 'shared/view/CenteredView'; // just a wrapper for View
import { FlexImage } from "shared/image/FlexImage"; // just a wrapper for Image
import { PLACEHOLDER } from "assets/images";
export const NavigationBar = (props) => {
return (
<View style={styles.barContainer}>
<View style={{flex: 1, backgroundColor: 'green'}} />
<CenteredView style={{flex: 2, backgroundColor: 'yellow'}}>
<FlexImage source={PLACEHOLDER} />
</CenteredView>
<CenteredView style={{flex: 1, backgroundColor: 'green'}}>
<Button title="Menu" color="#000" />
</CenteredView>
</View>
);
}
const styles = StyleSheet.create({
barContainer: {
flex: 1,
flexDirection: 'row',
backgroundColor: 'orange',
justifyContent: 'center',
// useless
alignment: 'fill',
width: '100%',
height: '100%',
padding: 0,
margin: 0,
marginLeft: 0
}
});
I assigned different colors to the containers and views to demonstrate the issue. The red column is what is wrong:
UPDATE: I noticed that I can swipe the complete page to the left so that the margin disappears and the very left of the content container (in aqua blue) disappears, leaving a blank area on the right (see picture below). This only works in the mobile device simulator in Chrome. On my Android phone I still have the margin but cannot swipe.

I fixed the issue by using the header property rather than headerTitle to declare NavigationBar as my custom header component.
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: styles.header,
header: props => <NavigationBar {...props} /> // <------- here
}}>
<Stack.Screen name="Home" component={Home} />
</Stack.Navigator>
</NavigationContainer>
);
}
I find the documentation of StackNavigator options a bit confusing. From my understanding, the difference between header and headerTitle is that headerTitle may also be a string and defaults to the scene title if the header component is not available. But that doesn't really explain the differences in layout. In the code example headerTitle is used.

Tried header Component but content went to the next line, would be glad if someone could suggest a better way to handle this case
Solution: I used headerTitle Component but with a little modification on right padding to workaround this issue.
I would like to suggest React navigation Team to provide more control over styles please.

Related

navigation.navigate is not a function. (In 'navigation.navigate("HomeScreen")', 'navigation.navigate' is undefined)

I m very new in react native and I m getting this error. Please help!
navigation.navigate is not a function. (In 'navigation.navigate("HomeScreen")', 'navigation.navigate' is undefined)
import { View, Text,Button,StyleSheet, TouchableOpacity } from 'react-native'
import React, {useState} from 'react'
import { NavigationContainer,CommonActions, useNavigation } from '#react-navigation/native';
const GetOtpButton = (navigation) => {
return (
<View >
<TouchableOpacity style = {styles.button} onPress={() => navigation.navigate("HomeScreen") } >
<Text style = {styles.text}>Log in</Text>
</TouchableOpacity>
</View>
)
}
const styles = StyleSheet.create({
button: {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: "white",
width: "100%",
height: 50,
borderColor: "#E13C72",
backgroundColor: "#E13C72",
borderWidth: 0.1,
borderRadius: 80,
// marginBottom: 40,
// marginVertical: 5,
// marginTop: 10,
},
text: {
justifyContent: 'center',
textAlign: 'center',
color: "white",
fontWeight: 'bold'
}
});
export default GetOtpButton
---------------------App.js------------------
import react from "react";
import { StatusBar } from "expo-status-bar";
import { SafeAreaView, StyleSheet, Text, View, Dimensions } from "react-native";
import SigninScreen from "./src/SigninScreen/SigninScreen";
import HomeScreen from "./src/HomeScreen/HomeScreen";
import { NavigationContainer, StackActions } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="SigninScreen" options={{headerShown: false}}>
<Stack.Screen name = 'SigninScreen' component={SigninScreen} options={{headerShown: false}}/>
<Stack.Screen name = 'HomeScreen' component={HomeScreen} options={{headerShown: false}}/>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
root: {},
container: {
flex: 1,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
backgroundColor: "#FFFFFF",
},
});
The component GetOptButton is not defined as a screen in the navigator, thus the navigation object will not be passed to it automatically by the navigation framework. Thus, you have multiple choices here.
Define it as a screen inside the navigator
<Stack.Screen name = 'GetOpt' component={GetOptButton} />
The framework will now pass the navigation object to the GetOptButton component. You can access it as follows.
const GetOtpButton = ({navigation}) => { ... }
Now, since GetOptButton seems to be a component rather than a screen to which you will navigate, it might not make much sense to define it inside the navigator.
Pass the navigation object from a screen which is defined in the navigator that uses the GetOptButton component
// this works, since `Homescreen` is defined as a screen in your navigator
const HomeScreen = ({navigation}) => {
return (
<GetOptButton navigation={navigation} />
)
}
Destructure the navigation object inside GetOptButton as shown above.
Use the useNavigation hook
For some components it might not make sense to pass the navigation object from its parent, e.g. if the component is deeply nested. For this cases you can use the useNavigation hook.
const GetOptButton = () => {
const navigation = useNavigation()
}
You are not showing how GetOtpButton is used but guessing that navigation is passed as prop to GetOtpButton you need to get to navigation like this
const GetOtpButton = ({navigation}) => {
return (
<View >
<TouchableOpacity style = {styles.button} onPress={() => navigation.navigate("HomeScreen") } >
<Text style = {styles.text}>Log in</Text>
</TouchableOpacity>
</View>
)
}
See that the difference is that navigation is surrounded by curly brackets, so later you get the parameter navigation inside the props

React Native Tab Navigator: empty space at bottom of tab bar

I'm using react-navigation#4.0.10 and react-native#0.63.5 in my React Native app, and when I use createBottomTabNavigator, there's a gap underneath the tab labels on iPhone 11 Pro. It does not do the same on iPhone 12. My code for the TabNavigatorOptions is as follows
const TabNavigatorOptions = {
tabBarPosition: 'bottom',
lazy: true,
tabBarOptions: {
activeTintColor: TabColors.activeColor,
inactiveTintColor: TabColors.labelColor,
bottomNavigationOptions: {
labelColor: TabColors.labelColor,
rippleColor: "white",
shifting: false,
activeLabelColor: TabColors.activeColor,
backgroundColor: TabColors.backgroundColor
},
style: {
height: 56, elevation: 8, position: 'absolute', left: 0, bottom: 0, right: 0
}
}
}
I've tried adding paddingBottom: 0 to the style object, but it makes no difference.
Does anyone know how I can approach this?
UPDATE:
If I add a red background in tabBarOptions -> style, with SafeAreaView I get this:
and if I remove SafeAreaView I get this
The only solution I could find for now is to remove the bottom inset from SafeAreaView. It's not a good solution in my opinion but at least it works:
import * as React from "react";
import { SafeAreaView } from "react-navigation";
export default function App() {
return (
<SafeAreaView
style={{ flex: 1 }}
forceInset={{ top: "always", bottom: "never" }}
>
<AppNavigator />
</SafeAreaView>
)
}
And if you are using react-native-safe-area-context:
import * as React from "react";
import { SafeAreaView } from 'react-native-safe-area-context';
export default function App() {
return (
<SafeAreaView
style={{ flex: 1 }}
edges={["right", "top", "left"]}
>
<AppNavigator />
</SafeAreaView>
);
}

React navigator not showing two route or all route

** I wanna to use react navigator on my app. Here I try to making two route and i want to see these. But i only see first route component. Theirs no button about 'def'. But copy those code from react navigator. i also found in snack emulator there also showing one route. does my code is wrong?? Advance thanks **
import { StatusBar } from 'expo-status-bar';
import React,{useState} from 'react';
import { StyleSheet, Text, View } from 'react-native';
import MultipleDateEntrys from './UI/Pages/MultipleDateEntrys.js'
import MaxCountrysEntry from './UI/Pages/MaxCountryData'
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const Stack = createStackNavigator();
function HomeScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>abc</Text>
</View>
);
}
function def() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>cde</Text>
</View>
);
}
export default function App() {
const title = useState('Corona Highlight')
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Overview' }}/>
<Stack.Screen
name="Mon"
component={def}
options={{ title: 'def' }}/>
</Stack.Navigator>
{/* <Text style={styles.title}>{title}</Text> */}
</NavigationContainer>
);
}
When using a stack navigator only one screen would be shown which is the intended behavior.
You will have to navigate to the other screen using a button or some similar component.
All screens get a navigation prop you can use it to navigate.
In order to navigate to a screen you have to use the navigation.navigate("ScreenName"). Here the screen name would be the name that you provide in the stack when you initialize.
function HomeScreen({navigation}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Navigate" onPress={()=>navigation.navigate("Mon")}/>
<Text>abc</Text>
</View>
);
}
If you want both the screen in the same view you can use a tab navigator, which would allow you to switch screen using a tab
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home"
component={HomeScreen}
options={{ title: 'Overview' }} />
<Tab.Screen name="Mon"
component={def}
options={{ title: 'def' }}/>
</Tab.Navigator>
);
}

Margin in React-Native not working properly

I was trying to make an app on React-Native
To start, I did mere basic where I wanted to make a bottom Navigation
This is what I did at start to test
import React, { Component } from 'react'
import {
StyleSheet,
View,
Text,
Image
} from 'react-native';
class MainScreen extends Component {
constructor () {
super ()
this.board = []
}
render () {
return (
<View style={MainScreenBox}>
<View style={GridBox}>
<Text> Testing..........</Text>
</View>
</View>
)
}
}
const styles = StyleSheet.create({
MainScreenBox: {
display: "flex",
flexDirection: "column"
},
GridBox: {
marginTop: "90%",
marginLeft: 5,
marginRight: 5
}
})
const {
GridBox,
MainScreenBox
} = styles
Here, I did something as simple as marginTop: "90%", expecting the text would come at bottom but it still remains half way on the screen.
Question: Any Idea why this could be happening?
Clean all styles and put this to MainScreenBox style:
const styles = StyleSheet.create({
MainScreenBox: {
flex: 1,
justifyContent:'flex-end'
}
})
and use style={styles.MainScreenBox} to apply in root View. You can refer to flex doc to see how FlexBox works. Also notice that flexDirection is column by default in react native you do not have to set it and display:"flex" neither.
Add a flex weight (flex:1) to your main container.
MainScreenBox: {
flex:1,
display: "flex",
flexDirection: "column"
},

React Native ScrollView with single child component

I want to have a fullscreen ScrollView which contains all of the contents of my Home screen in my app. The issue I'm facing is that it works as expected when I have two child components for the ScrollView (I added a Text component just to try it), but not if I just have the single content View that I want.
The contents of <View style={style.content}> are taller than the screen size, so it should scroll. But when I remove <Text style={{margin: 60, marginTop: 800}}>Here is some text</Text> the entire screen goes white.
Here's what I've heard about ScrollView:
It needs a limited height (but I should have that since all of the content is currently static (no dynamic data) and all of my components there have a fixed height)
You need to use {flex: 1} on the components inside the ScrollView (I have this style set on my Container component)
Here is my Home component:
import React, { Component } from "react";
import {
StatusBar, StyleSheet, View, ScrollView, Text
} from "react-native";
import { LinearGradient } from "expo";
import MaterialCommunityIcon from "react-native-vector-icons/MaterialCommunityIcons"
import Color from "color";
import globalStyles from "../config/globalStyles";
import Container from "../components/Container/Container";
import Header from "../components/Header/Header";
import FullWidthImage from "../components/Images/FullWidthImage";
import MainFeedbackText from "../components/Text/MainFeedbackText";
import MainStatistics from "../components/Statistics/MainStatistics";
import Button from "../components/Buttons/Button"
export default class Home extends Component {
render() {
return (
<ScrollView style={{borderColor:"red", borderWidth: 2}}>
<Container>
<View style={style.content}>
<MainFeedbackText/>
<Button
text="START NEW WORKOUT"
textStyle={{fontSize: 24}}
buttonStyle={{
backgroundColor: globalStyles.colors.green,
paddingHorizontal: 20,
}}
icon={<MaterialCommunityIcon style={style.settings} name="dumbbell" color="white" size={30}/>}
/>
<Button
text="My workouts"
textStyle={{fontSize: 24, paddingRight: 40}}
buttonStyle={{
backgroundColor: Color("black").fade(0.8),
paddingHorizontal: 20,
margin: 20
}}
icon={<MaterialCommunityIcon style={style.settings} name="format-list-bulleted" color="white" size={20}/>}
/>
<MainStatistics/>
</View>
{/**TODO: ScrollView breaks when removing this line*/}
<Text style={{margin: 60, marginTop: 800}}>Here is some text</Text>
<StatusBar translucent={false}/>
<LinearGradient colors={["transparent", globalStyles.colors.dark]} style={style.gradient}>
<View style={style.image}>
<FullWidthImage requireSource={require("./lifting.jpg")}/>
</View>
</LinearGradient>
<Header/>
</Container>
</ScrollView>
);
}
}
const style = StyleSheet.create({
image: {
opacity: 0.3,
zIndex: -1,
},
gradient: {
position: "absolute",
top: 0,
zIndex: -1
},
content: {
flex: 1,
position: "absolute",
top: 80,
height: 4000,
}
});
Set contentContainerStyle={{ flex: 1 }} for ScrollView to become fullscreen.

Resources