Navigate from stackNavigator to tabNavigator and vice versa - react-native

I do have Screen#1 which is part of stackNavigator, at some point i want to redirect to Screen#2 which part of tabNavigator
When i try with ({ navigator }) - navigator that is coming as a prop i get this error:
The action 'NAVIGATE' with payload {"name":"HomeScreen"} was not handled by any navigator.
So my question is how to jump from Screen#1 which is in stackNavigator to Screen#2 which part of tabNavigator
and my StackNavigator includes my TabNavigator like this:
TabNavigator:
const TabNavigator = () => {
const currentAccount = useRecoilValue(selectedAccountAtom);
const { isPinSetUp } = useRecoilValue(authAtom(currentAccount));
return (
<Tab.Navigator
screenOptions={{
headerShown: false,
}}
>
<Tab.Screen name={Navigators.WALLET} component={WalletNavigator} options={{ tabBarLabel: 'Tokens' }} />
/>
</Tab.Navigator>
);
};
StackNavigator:
<Stack.Navigator initialRouteName={initRoute} screenOptions={{ headerTitle: '', headerShown: false }}>
<Stack.Group>
<Stack.Screen name={Navigators.TAB_NAVIGATOR} component={TabNavigator} />
</Stack.Group>
</Stack.Navigator>

Related

How can I hide BottomTab in specific StackNavigator React native?

I'm usging Reactnative with React-Navigation 6
I created a Stack Navigator called HomeStacks and it has Home and Comment components.
And I put HomeStack in BottomTab.
At this time, I want to show BottomTab only in Home and hide BottomTab in Comment.
How can I do that?
This is my code
const HomeStack = createNativeStackNavigator();
const HomeStacks = () => {
return (
<HomeStack.Navigator>
<HomeStack.Screen
name="Home"
component={Home}
/>
<HomeStack.Screen
name="Comment"
component={Comment}
/>
</HomeStack.Navigator>
);
};
const BottomTab = createBottomTabNavigator();
<BottomTab.Navigator
screenOptions={{headerShown: false, tabBarShowLabel: false}}>
<BottomTab.Screen
name="HomeStacks"
component={HomeStacks}
options={{
<Ionicons name="home" size={28} style={{color: 'black'}} />
}}
/>
</BottomTab.Navigator>
Do this in your HomeStacks :
function HomeStacks({ navigation }) {
navigation.setOptions({ tabBarVisible: false })
return (
<View></View>
)
}

Cannot navigate from modal to full screen with no bottom tab in React Native Navigation

I have a bottom tab navigator with 2 icons of HomeScreen and MenuScreen, a separate screen (ProfileScreen), and a modal (ProfileLoginModal). The modal has a button that should redirect ProfileScreen. I put navigation.navigate("ProfileScreen") in the modal, but it's not doing anything, it's not even giving me an error.
But if I change it to one of the screens in the bottom tab nav, for example navigation.navigate("Root", {screen: "HomeScreen}), it works just fine. It just doesn't work with screens outside the bottom tab nav.
NOTE: I do not want a bottom tab on ProfileScreen, so putting it in a nested navigation is not an option.
Can anyone please help me understand what I'm doing wrong?
Navigation structure:
- BottomTabNavigator (Root)
- HomeScreen
- MenuScreen
- ProfileScreen
- ProfileLoginModal
Here is my navigation:
const Stack = createNativeStackNavigator<RootStackParamList>();
<NavigationContainer linking={LinkingConfiguration}>
<Stack.Navigator>
<Stack.Screen
name="Root"
component={BottomTabNavigator}
/>
<Stack.Screen
name="ProfileScreen" // ProfileScreen (destination)
component={ProfileScreen}
/>
<Stack.Group screenOptions={{ presentation: 'modal' }}>
<Stack.Screen
name="ProfileLoginModal" // Go to ProfileScreen from this modal
component={ProfileLoginModal}
/>
</Stack.Group>
</Stack.Navigator>
</NavigationContainer>
const BottomTab = createBottomTabNavigator<RootTabParamList>();
function BottomTabNavigator() {
return (
<BottomTab.Navigator
initialRouteName="HomeScreen"
screenOptions={...}
>
<BottomTab.Screen
name="HomeScreen"
component={HomeScreen}
options={...}
/>
<BottomTab.Screen
name="Menu"
component={MenuScreen}
options={...}
/>
</BottomTab.Navigator>
);
}
Typings:
export type RootStackParamList = {
Root: NavigatorScreenParams<RootTabParamList> | undefined;
ProfileScreen: undefined;
ProfileLoginModal: undefined;
};
export type RootStackScreenProps<Screen extends keyof RootStackParamList> =
NativeStackScreenProps<RootStackParamList, Screen>;
export type RootTabParamList = {
HomeScreen: undefined;
MenuScreen: undefined;
};
export type RootTabScreenProps<Screen extends keyof RootTabParamList> =
CompositeScreenProps<
BottomTabScreenProps<RootTabParamList, Screen>,
NativeStackScreenProps<RootStackParamList>
>;
I'm using React Native with Expo.
You can do something like this.
const HomeNavigator = () => {
return(
<Stack.Navigator>
<Stack.Screen
name="HomeScreen"
component={HomeScreen}
/>
<Stack.Screen
name="ProfileScreen" // ProfileScreen (destination)
component={ProfileScreen}
/>
<Stack.Group screenOptions={{ presentation: 'modal' }}>
<Stack.Screen
name="ProfileLoginModal" // Go to ProfileScreen from this modal
component={ProfileLoginModal}
/>
</Stack.Group>
</Stack.Navigator>)
}
function BottomTabNavigator() {
return (
<BottomTab.Navigator
initialRouteName="HomeNavigator"
screenOptions={...}
>
<BottomTab.Screen
name="HomeNavigator"
component={HomeNavigator}
options={...}
/>
<BottomTab.Screen
name="Menu"
component={MenuScreen}
options={...}
/>
</BottomTab.Navigator>
);
}

React navigation - react native - How to block drawer in Stack Navigator nested inside Drawer Navigator?

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.

How do I define multiple types of navigation containers in the same component?

I am new to the world of react native, so I need a hand with structuring my navigation pages. I want my app to have instagram like structure - that is the main page should be createBottomTabNavigator From there I have few different pages, and I can switch between them easily. Here is my code:
export default const MyTabs = ({ currentUser, navigation }) => {
return (
<Tab.Navigator initialRouteName="Home">
<Tab.Screen
name="Home"
component={HomeScreen}
/>
<Tab.Screen
name="Network"
component={NetworkScreen}
/>
<Tab.Screen
name="Profile"
component={ProfileScreen}
options={{
tabBarIcon: ({ color, size }) => (
<Entypo name="map" color={color} size={26} />
),
}}
/>
</Tab.Navigator>
);
};
Then in my App.js file I call it like this:
return (
<Provider store={store}>
<NavigationContainer>
<MyTabs />
</NavigationContainer>
</Provider>
);
The above code works fine. The problem is when I want to perform more complicated navigation. For instance, in my home page I have list of objects and once, I click on these objects I want to navigate to a new page - with Stack navigator. How do I do so, and where should I define this page? Do I define it somehow inside my Home page, or inside my App.js page? In my mind it should be something like this:
<Stack.Screen
name="Details"
component={DetailScreen}
/>
I tried to put it inside App.js, something like this:
<Provider store={store}>
<NavigationContainer>
<MyTabs />
<Stack.Navigator>
<Stack.Screen
name="Meetup Details"
component={MeetupDetails}
options={{
headerTitle: "Meetup Details",
}}
/>
</Stack.Navigator>
</NavigationContainer>
</Provider>;
But ofc it didn't work.
Also I have another question - in my bottom tab I can navigate to My profile screen and that works fine. But I want to view other profiles as well. So my bottom tab is reserved for my own profile, but how do I define another Profile screen, which can go to other user's profile? Does it need to be with stack navigator? Does it again need to be defined in App.js? Is it ok to define two different navigators (bottom tab and stack) with the same page (Profile)?
Usually, when you have a bottom tab navigator, each tab will be a StackNavigator
export default const MyTabs = ({ currentUser, navigation }) => {
return (
<Tab.Navigator initialRouteName="Home">
<Tab.Screen
name="Home" // here this would be BottomTabRoutes.HOME
component={HomeNavigator}
/>
<Tab.Screen
name="Network" // here this would be BottomTabRoutes.NETWORK
component={NetworkNavigator}
/>
<Tab.Screen
name="Profile" // here this would be BottomTabRoutes.PROFILE
component={ProfileNavigator}
options={{
tabBarIcon: ({ color, size }) => (
<Entypo name="map" color={color} size={26} />
),
}}
/>
</Tab.Navigator>
);
};
And then your MeetupDetails screen will be contained inside the HomeNavigator which is a StackNavigator
export const HomeNavigator = () => {
return (
<Stack.Navigator
screenOptions={{
...
}}>
<Stack.Screen
name={HomeRoutes.HOME}
component={Home}
options={{
headerStyle: styles.header,
title: 'Home'
}}
/>
<Stack.Screen
name={HomeRoutes.MEETUP_DETAILS}
component={MeetupDetails}
options={{
headerStyle: styles.header,
title: 'Meetup Details'
}}
/>
</Stack.Navigator>
}
Also, I would suggest to store your routes names in an enum, in this way it will be easier and their possible route props in a tpye
Something like this:
export enum BottomTabRoutes {
HOME = 'Home',
NETWORK = 'Network',
PROFILE = 'Profile',
}
export type BottomTabRouteProps = {
[BottomTabRoutes.HOME]: {title: string}; // this is another parameter example
[BottomTabRoutes.PROFILE]: undefined;
[BottomTabRoutes.NETWORK]: undefined;
};
And the same for each Stack Navigator:
export enum HomeRoutes = {
HOME = 'Home',
MEETUP_DETAILS = 'Meetup Details',
}
export type HomeRouteProps = {
[HomeRoutes.HOME]: undefined
[HomeRoutes.MEETUP_DETAILS: { someParamName: SomeType } // this might be a parameter you'll send through navigation
}
// Therefore, your MeetupDetails screen will look something like this:
export const MeetupDetails = (props: StackScreenProps<HomeRouteProps, HomeRouteProps.MEETUP_DETAILS>,) => {
const navigationParam = props.route.params.someParamName
return (
<View>
...
</View>
)
}
EDIT
Regarding the Profile Screen, I believe you can have something like this:
export const MainNavigator = () => {
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name={MainNavigatorRoutes.HOME} component={HomeNavigator} />
<Stack.Screen name={MainNavigatorRoutes.USER} component={UserNavigator} />
</Stack.Navigator>
);
};
export const UserNavigator = () => {
return (
<Stack.Navigator
screenOptions={({navigation}) => ({
...)}>
<Stack.Screen
name={UserNavigatorRoutes.SOME_OTHER_SCREEN}
component={UserSettings}
/>
<Stack.Screen
name={UserNavigatorRoutes.USER_PROFILE}
component={UserProfile}
/>
</Stack.Navigator>
);
};
export const HomeNavigator = () => {
return (
<Stack.Navigator screenOptions={{title: ''}} >
<Stack.Screen
name={HomeNavigatorRoutes.HOME_TABS}
component={HomeTabNavigator}
options={{
headerShown: false,
}}
/>
</Stack.Navigator>
);
};
export const HomeTabNavigator = () => (
<Stack.Navigator >
<Stack.Screen
name={HomeTabNavigatorRoutes.TAB_NAVIGATOR}
component={MyTabs}
options={{headerShown: false}}
/>
</Stack.Navigator>
Basically, the structure will look like this: MainNavigator = { HomeNavigator, UserNavigator }. In this way, each navigator will be separate and you should be able to navigate to any screen from UserNavigator even if you are in a screen from UserNavigator

React navigation 5 - header is not shown

Trying to update my app to react navigation 5 and been confronting some issues.
First of all, the header does not show up. Snips from the code:
[from App.js]
const Tab = createBottomTabNavigator();
function App() {
return (
<NavigationContainer>
<Tab.Navigator >
<Tab.Screen name="Home" component={HomeScreen} options={{ title:'some title' }}/>
<Tab.Screen name="Upload" component={UploadScreen} />
<Tab.Screen name="Find" component={FindScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default App;
and the style of the current screen:
<View style={{flex:1, flexDirection:'column',justifyContent:'space-between'}}>
Here is a screenshot of the app on an Android emulator (and it looks the same on my phone):
As you can see, the header is not shown, the tab navgiation does not right, and so are the buttons (something changed about their background). I did not change anything in the app besides upgrading to react-navigation 5..
Thanks for the help!
Tab navigators do not have header support. You have to wrap your tab navigator inside a stack navigator.
import { createStackNavigator } from "#react-navigation/stack";
// ... other imports
export const App = () => {
return (
<NavigationContainer>
<StackNavigator />
</NavigationContainer>
);
}
const Stack = createStackNavigator();
const StackNavigator = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Tabs" component={TabNavigator} />
</Stack.Navigator>
);
}
const Tab = createTabNavigator();
const TabNavigator = () => {
return (
<Tab.Navigator >
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Upload" component={UploadScreen} />
<Tab.Screen name="Find" component={FindScreen} />
</Tab.Navigator>
);
}