How to show tabs AFTER user has logged in (separate navigators) - react-native

I want a user to log in BEFORE they are taken to the "Home" screen. Once they hit the home screen, the tabs are shown. I do not want to show the tabs during login/signup.
Here is what I have so far:
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const Switch = createSwitchNavigator();
const AppContainer = createAppContainer();
function AuthNavigation() {
return (
<Stack.Navigator headerMode="none" initialRouteName="Login">
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Signup" component={Signup} />
</Stack.Navigator>
)
}
function MyTabs() {
return (
<Tab.Navigator
initialRouteName="Home"
activeColor="#161F3D"
inactiveColor="#B8BBC4">
<Tab.Screen
name="Home"
component={Home}
// options={{
// tabBarIcon: <Icon name="home" size={24} />
// }}
/>
<Tab.Screen
name="Leaderboard"
component={Leaderboard}
// options={{
// tabBarIcon: <Icon name="trophy" size={24} />
// }}
/>
<Tab.Screen
name="Create"
component={Create}
// options={{
// tabBarIcon: <Icon name="add" size={40} />
// }}
/>
<Tab.Screen
name="Notifications"
component={Notification}
// options={{
// tabBarIcon: <Icon name="notifications-circle-outline" size={24} />
// }}
/>
<Tab.Screen
name="Profile"
component={Profile}
// options={{
// tabBarIcon: <Icon name="person" size={24} />
// }}
/>
</Tab.Navigator>
);
}
export default function App() {
return (
<AppContainer>
<Switch.Navigator headerMode="none" initialRouteName="Auth">
<Switch.Screen name="Auth" component={AuthNavigation()} />
<Switch.Screen name="Tabs" component={MyTabs()} />
</Switch.Navigator>
</AppContainer>
);
}
This results in the following error:
TypeError: undefined is not an object (evaluating 'Object.keys(routeConfigs)')
How can I build these two navigators so the auth and the tab navigators are separate?

You only need to pass the reference of the component. You don't need to execute it. It should look like this -
<Switch.Screen name="Auth" component={AuthNavigation} />
<Switch.Screen name="Tabs" component={MyTabs} />

Related

What's the correct approach for having a list/detail view with React Native Navigation Bottom Tab?

I have something like:
const Tab = createBottomTabNavigator<DefaultTabbedParamList>();
const DefaultTabbedNavigation = () => {
return (
<>
<Tab.Navigator initialRouteName='Home' screenOptions={{
unmountOnBlur: true,
}}>
<Tab.Screen name="Home" component={HomeScreen} options={{
...defaultOptions,
tabBarIcon: ({ color, size, focused }) => (
<Icon as={Ionicons} name={`home${focused ? `` : `-outline`}`} size={size} color={color} />
)
}} />
...
</Tab.Navigator>
</>
);
}
When a user clicks to a detail view from Home (or any other tab), I want to load a detail view with the currently selected tab remaining.
What's the correct approach to handle this?
One idea I had was to have a StackNavigator in HomeScreen that includes a Detail screen. But it seems repetitive to do for every screen, no?
You can do something like this :-
<Tab.Navigator initialRouteName='Home' screenOptions={{
unmountOnBlur: true,
}}>
<Tab.Screen name="Home" component={HomeScreen} options={{
...defaultOptions,
tabBarIcon: ({ color, size, focused }) => (
<Icon as={Ionicons} name={`home${focused ? `` : `-outline`}`} size={size} color={color} />
)
}} />
// Something like this.
<Tab.Screen name="Home2" children={({route}) => <route?.name />} ...{otherProperties}/>
...
</Tab.Navigator>
Note:- To use this kind of approch your routeName and componentName should be same.
How about this?
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name={"Tabs"} component={Tabs} />
<Stack.Screen name={"Detail"} component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
Yeah, you'll likely want to define a StackNavigator for each tab. It's a bit repetitive, but that's been a theme of my experience with RN.
You can do something like:
const HomeStackNavigator = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
);
};
const OtherStackNavigator = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Other" component={OtherScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
);
};
const DefaultTabbedNavigation = () => {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStackNavigator} />
<Tab.Screen name="Other" component={OtherStackNavigator} />
</Tab.Navigator>
)
}

Hiding tab bar with nested navigation

I used nested navigation for my problem. Currently, however, the tab bar is only displayed on pages listed in TabNavigator. Is it possible to display on other pages as well? It is possible to display the tab bar also on screen5. I also have screen6 where I don't want to display the tab bar, so I solve it this way.
App.js
<NavigationContainer
ref={ref}
>
<Navigation />
</NavigationContainer>
Tab.js
const Tab = createBottomTabNavigator();
const Tabs = (props) => {
return(
<Tab.Navigator>
<Tab.Screen name="Screen1" component={Screen1} />
<Tab.Screen name="Screen2" component={Screen2} />
<Tab.Screen name="Screen3" component={Screen3} />
<Tab.Screen name="Screen4" component={Screen4} />
</Tab.Navigator>
);
}
<Stack.Navigator>
<Stack.Group screenOptions={{ headerShown: false }}>
<Stack.Screen name="Screen1" component={Tabs}/>
<Stack.Screen name="Screen2" component={Screen2}/>
<Stack.Screen name="Screen3" component={Screen3}/>
<Stack.Screen name="Screen4" component={Screen4}/>
<Stack.Screen name="Screen5" component={Screen5}/>
<Stack.Screen name="Screen6" component={Screen6}/>
</Stack.Group>
</Stack.Navigator>
Screen4
const Screen4 = ({navigation}) => {
return (
<SafeAreaView>
<Pressable onPress={() => {navigation.navigate('Screen5')}}>
<Text>Screen5</Text>
</Pressable>
</SafeAreaView>
);
}
export default Screen4;
I think you are aksing about this.
const Tab = createBottomTabNavigator();
const Stack = createStackNavigator();
const HomeNavigator = () => {
<Stack.Navigator>
<Stack.Screen name="Screen1" component={Screen1} />
<Stack.Screen name="Screen2" component={Screen2} />
</Stack.Navigator>;
};
const ProfileNavigator = () => {
<Stack.Navigator>
<Stack.Screen name="Screen3" component={Screen3} />
<Stack.Screen name="Screen3" component={Screen3} />
</Stack.Navigator>;
};
const Tabs = (props) => {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeNavigator} />
<Tab.Screen name="Profile" component={ProfileNavigator} />
<Tab.Screen name="YourOtherNavigator" component={YourOtherNavigator} />
</Tab.Navigator>
);
};

React Native how to programmatically navigate to a stack screen that is on a tab navigator

I have searched tirelessly for an answer to this issue.
I have a Tab Navigator:
const AppNavigator = () => {
return (
<Tab.Navigator initialRouteName="Search">
<Tab.Screen
name="Home"
children={() => <HomeScreen />}
options={{
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="home" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Search"
children={() => <SearchContainerNavigator />}
options={{
tabBarIcon: ({ color, size }) => (
<FontAwesome5 name="search" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Feedback"
children={() => <FeedbackScreen />}
options={{
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="email" color={color} size={size} />
),
}}
/>
</Tab.Navigator>
);
};
And this Stack navigator:
const SearchContainerNavigator = () => (
<Stack.Navigator screenOptions={{ headerShown: false}} initialRouteName="SearchSelect">
<Stack.Screen name="SearchSelect" component={SearchSelectScreen} />
<Stack.Screen name="AircraftSearch" component={AircraftSearchScreen} />
<Stack.Screen name="AircraftResults" component={AircraftResultsScreen} />
<Stack.Screen name="AircraftDetail" component={AircraftDetailScreen} />
<Stack.Screen name="BrandSearch" component={BrandSearchScreen} />
<Stack.Screen name="BrandResults" component={BrandResultsScreen} />
<Stack.Screen name="BrandDetail" component={BrandDetailScreen} />
<Stack.Screen name="Favourites" component={FavouritesScreen} />
</Stack.Navigator>
)
My issue is how do I programmatically navigate from HomeScreen tab to the AircraftDetailsScreen on the Search Tab??
Thanks in advance.
EDIT:
HomeScreen is a class.
handleAircraftSelect = (aircraft_id) => {
this.props.navigation.navigate("Search", {
screen: "AircraftDetail",
params: { id: aircraft_id },
});
};
navigation.navigate('Search', {screen: 'AircraftDetail'});
https://reactnavigation.org/docs/nesting-navigators/#navigating-to-a-screen-in-a-nested-navigator
For react-navigation 6.x you can use this like below:
navigation.jumpTo('Profile', { name: 'Michaƛ' });

Use DrawerNavigator and StackNavigator together [duplicate]

This is my tab navigator:
<Tab.Navigator initialRouteName="Home" backBehavior="initialRoute">
<Tab.Screen
name="Science"
component={Science}
options={{
tabBarLabel: 'Science',
tabBarIcon: ({color, size}) => (
<Image source={require('../../assets/images/science-tab.png')} />
),
}}
/>
<Tab.Screen name="Dashboard" component={Dashboard} />
</Tab.Navigator>
This is DrawerNavigator:
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Notifications" component={NotificationsScreen} />
</Drawer.Navigator>
And this is my root navigator: Below Bottomnavigation is the tab navigator.
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="BottomNavigation"
component={BottomNavigation}
options={{title: this.createTitle()}}
/>
</Stack.Navigator>
</NavigationContainer>
I recommend you to make your TabNavigator a screen of DrawerNavigator
You can do something like this:
function TabNavigator({navigation}) {
return (
<Tab.Navigator>
// Your tab screens
</Tab.Navigator>
);
}
function DrawerNavigator() {
return (
<Drawer.Navigator>
<Drawer.Screen name="TabNavigator" component={TabNavigator} />
</Drawer.Navigator>
);
}
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="DrawerNavigator" component={DrawerNavigator} />
</Stack.Navigator>
</NavigationContainer>
);
};
If you want to open your drawer, you can call navigation.openDrawer() in your TabNavigator.
Update to address label issue
You can create a drawer content component to override the default behavior of adding the DrawerNavigator screens' labels as the content of the drawer.
function CustomDrawerContent(props) {
return (
<DrawerContentScrollView {...props}>
<DrawerItem
label="Home"
onPress={() => props.navigation.navigate('Home')}
/>
// ...
</DrawerContentScrollView>
);
}
Then you need to change the DrawerNavigator to this:
function DrawerNavigator({route}) {
return (
<Drawer.Navigator
drawerContent={(props) => <CustomDrawerContent {...props} />}>
<Drawer.Screen name="TabNavigator" component={TabNavigator} />
<Drawer.Screen name="Home" component={Home} />
</Drawer.Navigator>
);
}
So you can add new screens to your DrawerNavigator and navigate to them using the DrawerItem onPress function.
Of course make sure to import DrawerContentScrollView, DrawerItemList and DrawerItem from #react-navigation/drawer.
For more info look at: https://reactnavigation.org/docs/drawer-navigator/#providing-a-custom-drawercontent.

Hide Custom Header in specific screen with headerMode float

I have 3 screen in my app: "Home, Contacts, Profile". I created a custom header to show in Home and Contacts, but not in Profile screen. The problem is: my custom header don't hide in Profile Screen. If I remove my custm header to use the default header, it hides, but when I back to my custom header this doesn't happen.
App.js
<NavigationContainer ref={navigationRef}>
<Stack.Navigator
headerMode="float"
initialRouteName="Home"
screenOptions={{
header: props => <CustomHeader {...props} />
}}>
<Stack.Screen
name="Home"
component={Home}
options={{
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS
}}/>
<Stack.Screen name="Contacts" component={Contacts}
options={{
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS
}}/>
<Stack.Screen
name="Profile"
component={Profile}
options={{
headerShown: false
}} />
</Stack.Navigator>
</NavigationContainer>
You can provide screen wise header like.
<NavigationContainer ref={navigationRef}>
<Stack.Navigator
headerMode="float"
initialRouteName="Home">
<Stack.Screen
name="Home"
component={Home}
options={{
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
header: (props) => <CustomHeader {...props} />
}}
/>
<Stack.Screen
name="Contacts"
component={Contacts}
options={{
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
header: (props) => <CustomHeader {...props} />
}}
/>
<Stack.Screen
name="Profile"
component={Profile}
options={{
headerShown: false,
header: null,
}}
/>
</Stack.Navigator>
</NavigationContainer>;
Or you can create custom function for all header
function getHeader(route, props) {
const routeName = route.state
?
route.state.routes[route.state.index].name
: || 'Home';
switch (routeName) {
case 'Home':
case 'Contacts':
return <CustomHeader {...props} />
case 'Profile':
return null;
}
}
and use it like
<Stack.Screen
name="Profile"
component={Profile}
options={({ route }) => ({
header: (props)=> getHeader(route, props),
})}
/>
Source : https://reactnavigation.org/docs/screen-options-resolution