hello community im relatively new to React native I want to show the arrow left to go back to screens !! when i run my code it doesnt show up , and i think i have to disable my headershown:flase and i put it on commentary ! but unfortunately i have this error Objects are not valid as a React child (found:Object with keys{left}) This is my code :
App.js
<Stack.Navigator
initialRouteName="Home"
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerShown:false
}}
/>
<Stack.Screen
name="Sign In"
component={SignIn}
options={{
headerShown:false,
header: ({ goBack }) => ({
left: ( <Icon name={'arrowleft'} onPress={ () => { goBack() } } /> ),
})
}}
/>
<Stack.Screen
name="Registration"
component={Registration}
options={{
headerShown:false,
header: ({ goBack }) => ({
left: ( <Icon name={'arrowleft'} onPress={ () => { goBack() } } /> ),
})
}}
/>
</Stack.Navigator>
</NavigationContainer>
Try modifying your options like so:
options={{
headerTransparent: true,
headerBackImage: () => <Icon name={'arrowLeft'} />,
}}
This will change only the back icon (you might add some styling to position it as you want), and ReactNavigation will handle press events.
I don't think you want to use headerShown: false as this will hide the entire navigation bar, back button included.
The error you are receiving is due to the fact that the API changed a few versions back and the header property now requires a component to render, not an object.
Related
I'm currently struggeling with a behavior on react-navigation for react-native. My Navigation has a bottom tab navigator which has four entries represented as Stacks.
const Tabs = createBottomTabNavigator<AppParamList>();
export const AppTabs: React.FC<AppTabsProps> = () => {
return (
<Tabs.Navigator>
<Tabs.Screen
name="Store"
options={{
title: i18n.t("STORE.STORE"),
header: () => null,
}}
component={StoreStack}
/>
<Tabs.Screen
name="Favorites"
options={{ title: i18n.t("FAVORITES"), header: () => null }}
component={FavoriteStack}
/>
<Tabs.Screen
name="Orders"
options={{ title: i18n.t("ORDERS"), header: () => null }}
component={OrdersStack}
/>
<Tabs.Screen
name="Account"
options={{ title: i18n.t("ACCOUNT"), header: () => null }}
component={AccountStack}
/>
</Tabs.Navigator>
);
};
One of those Stacks, the first one, includes a Stack which is represented as a fullscreen modal ( The last one called checkout).
const Stack = createNativeStackNavigator<StoreParamList>();
export const StoreStack: React.FC<StoreStackProps> = () => {
return (
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
options={{ header: () => null }}
name="Home"
component={HomeScreen}
/>
<Stack.Screen
options={{ header: () => null }}
name="Product"
component={ProductScreen}
/>
<Stack.Screen
options={{ header: () => null, presentation: "fullScreenModal" }}
name="Checkout"
component={CheckoutStack}
/>
</Stack.Navigator>
);
};
Now this does work as intended on ios, where its just opening a fullscreen modal only showing my checkout stack. My problem is, that, since the modal stack is contained within my TabsNavigator, on android it opens the modal while still presenting the TabNavigator on the bottom. Does anyone have a advice on how I can go around this problem?
There is a flag on the tabsNavigator, where you can remove the tabBar
options={{
tabBarStyle: { display: "none" },
}}
, however that is only on the upper level and is not available within my Tabs.Screens. I was thinking of moving up the modal to that level, but then it displays a fifth entry in my tabsBar and also complicates the navigation with Typescript.
In my react native app I have a stack navigator nested inside a drawer navigator. I want the drawer to be disabled in the stack navigator pages. I'm using react navigation 6.
In the docs (https://reactnavigation.org/docs/drawer-navigator/#options) I see there are two options for this: gestureEnabled and swipeEnabled. But these can only be used in drawer screens, not in stack screens like my case.
My code is as following:
const Stack = createNativeStackNavigator<RootStackParamList>();
const Drawer = createDrawerNavigator<RootTabParamList>();
const loginStack = () => (
<Stack.Navigator>
<Stack.Screen name="LandingScreen" component={LandingScreen} options={{ headerShown: false }} />
<Stack.Screen name="LoginScreen" component={LoginScreen} options={{ headerShown: false }} />
<Stack.Screen
name="RegisterScreen"
component={RegisterScreen}
options={{ headerShown: false }}
/>
</Stack.Navigator>
);
return (
<NavigationContainer>
<Drawer.Navigator
screenOptions={{
drawerStyle: { backgroundColor: 'white' },
drawerPosition: 'right',
}}
>
{!user ? (
<Drawer.Screen
name="PublicStack"
component={loginStack}
// options={{headerShown: false}}
options={({ route }) => {
const routeName = getFocusedRouteNameFromRoute(route);
if (
routeName === 'LandingScreen' ||
routeName === 'LoginScreen' ||
routeName === 'RegisterScreen'
)
return { swipeEnabled: false, gestureEnabled: false };
return { swipeEnabled: true, gestureEnabled: true };
}}
/>
) : (
<>
<Drawer.Screen
name="Search cocktails"
component={HomeScreen}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="Profile"
component={ProfileScreen}
initialParams={{ userParam: null }}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="Publish a recipe"
component={PublishRecipeScreen}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="Favorites"
component={FavoritesScreen}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="Published recipes"
component={PublishedRecipesScreen}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="Log out"
component={CustomDrawerContent}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="CocktailDetailScreen"
component={CocktailDetailScreen}
options={{
header: () => <Header />,
drawerLabel: () => null,
title: undefined,
}}
/>
</>
)}
</Drawer.Navigator>
</NavigationContainer>
);
I've tried setting the mentioned options directly on the loginStack drawer screen, like:
<Drawer.Screen
name='PublicStack'
component={loginStack}
options={{swipeEnabled: false, gestureEnabled: false}}}
/>
But didn't work.
I've also seen this answer (How to disable drawer inside Stack Navigator nested inside Drawer Navigator?) and tried to implement something similar (what my code looks like right now) but still didn't work.
Full code can be found here: https://github.com/coccagerman/mixr
Thanks!
I was stuck with the same thing these days. I didn't find a solution to use getFocusedRouteNameFromRoute(route) and took a different approach.
The first thing is block the Drawer in the whole app:
<Drawer.Navigator screenOptions = {{ swipeEnabled: false }}>
<Drawer.Screen name="Screen1" component={StackScreen1} />
<Drawer.Screen name="Screen2" component={StackScreen2} />
</Drawer.Navigator>
Then, you enable the Drawer on the screens you need, like this:
useFocusEffect(
useCallback((() => {
// From a Stack screen, the Drawer is accessed.
const parent = navigation.getParent()
parent?.setOptions({ swipeEnabled: true })
// It returns to the initial state.
return () => parent?.setOptions({ swipeEnabled: false })
}, [navigation])
)
In case you have to enable the Drawer on many screens, it can be done the other way around. Enable the Drawer in the whole app, and block it only in the desired ones.
I know that maybe it's not the best solution but I hope it helps you. Saludos!
In my case, I only want to have swipe enabled for the first screen in the stack navigator that's nested in the drawer navigator.
I span a group around the drawer screens, get the focused route name and only enable swipe if it's the first route of the stack navigator.
<Drawer.Group
screenOptions={({ route }) => ({
swipeEnabled: getFocusedRouteNameFromRoute(route) === 'NameOfFirstScreenInStack',
})}
>
{..}
</Drawer.Group>
getFocuesRouteNameFromRoute isn't well documented but at least there's a guide working with it.
I am using createBottomTabNavigation and I have 4 screens which I can access by pressing the tab bar screen in the bottom of my screen. I am also using Stack Navigation to display the title of the screen there and also all screens have the same settings icon.
I could make the same function in every screen that takes care of onPress event on the Settings icon but that is repetitive and I don't want to do it.
My question is - is there a way for me to pass a function as a props from the App component which holds the bottom navigation to every screen?
App screen code:
<NavigationContainer >
<Tab.Navigator initialRouteName="Home" tabBarOptions={{
activeTintColor: '#FF9F0A',
inactiveTintColor:'white',
style: {
backgroundColor:'#000000',//color you want to change
borderTopWidth: 0,
paddingTop:5,
paddingBottom:5,
},
}}>
<Tab.Screen name="Home" component={Home} options={{
tabBarLabel: 'HOME',
tabBarIcon: ({ color, size }) => (
<HomeTabIcon name="home" color={color} size={size} />
),
}}/>
<Tab.Screen name="Controls" component={Controls} options={{
tabBarLabel: 'CONTROLS',
tabBarIcon: ({ color, size }) => (
<ControlsTabIcon name="controls" color={color} size={size} />
),
}}/>
<Tab.Screen name="Charging" component={Charging} options={{
tabBarLabel: 'CHARGING',
tabBarIcon: ({ color, size }) => (
<ChargingTabIcon name="charging" color={color} size={size}/>
),
}}/>
Charging screen:
function Charging() {
return (
<View style={globalStyles.container}>
<Text>Charging</Text>
<StatusBar style="auto" />
</View>
);
}
export default function ChargingStackScreen() {
return (
<ChargingStack.Navigator>
<ChargingStack.Screen name="CHARGING" component={Charging} options={{
headerRight: () => (
<View style={globalStyles.headerRight}>
<SettingsIcon />
</View>
),
headerTitleAlign:'left',
headerTintColor: 'white',
headerTitleStyle: globalStyles.headerTitle,
headerStyle: globalStyles.header
}}/>
</ChargingStack.Navigator>
);
}
So what I think you should do is have the header as a separate component, where you can use the useNavigation hook (assuming you're using the latest version of react-navigation), and have the settings button do the navigation you need. Then just render the header component once above the Tab.Navigator instead of rendering it on every screen.
Something like:
function Header(props){
const navigation = useNavigation();
// component code
<SettingsButton onPress={() => navigation.navigate(whatever)}/>
}
And then I am pretty sure (not 100% though) that you can render it in your NavigationContainer, something like:
<NavigationContainer >
<Header/>
<Tab.Navigator initialRouteName="Home" tabBarOptions={{
// rest of the code
How can I show BottomTabNavigation even on stacked screen? I have tried this for a few hours but really don't get it to work as expected.
So the thing I want to happen is, if I navigate to say for example the Title Screen, I still want to show the BottomTabNavigation. Any suggestions?
I can of course create a new navigation, but then it is sliding in from the side.
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const HomeTabNavigator = () => {
return (
<Tab.Navigator
tabBarOptions={{
labelStyle: {textTransform: 'uppercase'},
style: {
backgroundColor: '#111111', //Färger på footerbar
borderTopColor: 'transparent',
borderBottomColor: 'transparent',
},
}}>
<Tab.Screen
name={'Concerts'}
component={ConcertsScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image
source={require('../../assets/icons/concerts.png')}
size={25}
/>
),
}}
/>
<Tab.Screen
name={'Docs'}
component={DocumentiesScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/docs.png')} size={25} />
),
}}
/>
<Tab.Screen
name={'Profile'}
component={ProfileScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/user.png')} size={25} />
),
}}
/>
</Tab.Navigator>
);
};
const Router = () => {
const {token, setToken} = useContext(TokenContext);
const {userFav, addFav, getFav} = useContext(UserContext);
const [isLoading, setLoading] = useState(true);
useEffect(() => {
setLoading(false);
setTimeout(() => {}, 1000);
}, []);
return (
<NavigationContainer>
{token ? (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerTransparent: true,
noBorder: true,
}}
headerMode="float">
<Stack.Screen name={' '} component={HomeTabNavigator} />
<Stack.Screen name={'Concerts'} component={ConcertsScreen} />
<Stack.Screen name={'User Profile'} component={ProfileScreen} />
<Stack.Screen
name={'FavouritesScreen'}
component={FavouritesScreen}
/>
<Stack.Screen name={'Docs'} component={DocumentiesScreen} />
<Stack.Screen name={'AccountScreen'} component={AccountScreen} />
<Stack.Screen name={'Home of'} component={SearchScreen} />
<Stack.Screen name={'Artist'} component={ArtistScreen} />
<Stack.Screen name={'Title'} component={Videos} />
<Stack.Screen name={'PlayVideo'} component={PlayVideo} />
</Stack.Navigator>
) : (
<LoginScreen />
)}
</NavigationContainer>
);
};
You need to nest all your stack screens inside a tab screen.
The BottomTabNavigator disappear because you leave your Tab.Navigator component.
I hope this helps. If you want to navigate between screens that are related to a specific tab button, and have that tab button remain active while moving between these screens, you should set up a StackNavigation within that tab's component. By doing so, the tab button will remain active while navigating within its related screens.
On the other hand, if you want the TabNavigation to be visible throughout the whole application but some screens should not be displayed as tabs, you can add all screens inside the TabNavigation and specify in the options for those screens not to be displayed as tab buttons. That way, while in the screen without a tab button, the tabs will still be visible but none will be active. For example, you can do this for a screen called 'Title':
<Tab.Screen
name={'Title'}
component={Videos}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/user.png')} size={25} />
),
tabBarButton: () => null <---- *this causes it to have no button*
}}
/>
I hope this helps!
I have a simple page with list of students. Once I click on each student profile, I want to go to a new page, where the title of the page will be the name of the student and where I will have a button on the right. All of this should be done in the header. So I put the following code:
<Stack.Screen name="Profile" component={ProfileScreen}
options={({ route }) => ({ title: route.params.name })}
/>
Which correctly displays the name of the student on the header. Then I wanted to add the button on the right and as written here I changed my code to:
<Stack.Screen name="Profile" component={ProfileScreen}
options={{ headerTitle: ({route}) => ({ title: route.params.name }),
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>),
}}
/>
And now I have the following error: TypeError: undefined is not an object (evaluating 'route.params')
Can someone advice how to add both custom title and button
You will have to pass it like below
options={({ route }) => ({
title: route.params.name,
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
})}
There are two methods by which you can set up the custom header
You can set up a custom button like this
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>
);
}
You can hide the header by navigation
options: {
header: null,
}
and can also create custom header and import it to any component you can pass props from every component to that one custom header