I'm making an app in which I have more than one type of navigation, but I'm not sure how to implement it until now, my apps only had one type of navigation. I tried to read the react-navigation docs that talked about this particular implementation, but I couldn't do it. Can someone help me?
So you guys understand, I'm trying to implement a Stack navigator inside a Tab navigator so that when the user clicks on a point in a map, a details page shows up to him.
Here's what I tried to do
import React from 'react'
import { NavigationContainer } from '#react-navigation/native'
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs'
import { createStackNavigator } from '#react-navigation/stack'
import { Map } from '../pages/Map'
import { Detail } from '../pages/Detail'
import FiIcon from '#expo/vector-icons/Feather'
const Tab = createBottomTabNavigator()
const Stack = createStackNavigator()
export const UsersRoutes = () => {
return (
<>
<NavigationContainer>
<Tab.Navigator
initialRouteName='Mapa'
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Mapa') {
iconName = 'map'
} else if (route.name === 'Pesquisar') {
iconName = 'search'
} else {
iconName = 'user'
}
return <FiIcon name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
activeTintColor: 'black',
inactiveTintColor: 'gray',
}}
>
<Tab.Screen name='Mapa' component={Map} />
<Tab.Screen name='Pesquisar' component={() => <Text>Search</Text>} />
<Tab.Screen name='Perfil' component={() => <Text>Profile</Text>} />
</Tab.Navigator>
</NavigationContainer>
<NavigationContainer>
<Stack.Navigator headerMode='none'>
<Stack.Screen name='Mapa' component={Map} />
<Stack.Screen name='Detalhes' component={Detail} />
</Stack.Navigator>
</NavigationContainer>
</>
)
}
What you can do is:
const MapaStack = () => (
<Stack.Navigator>
<Stack.Screen name="Mapa" component={MapaScreen} />
<Stack.Screen name="AnyScreen" componnent="AnyScreen" />
</Stack.Navigator>
);
const Tabs = () => (
<Tab.Navigator>
<Tab.Screen name="Mapa" component={MapaStack} />
<Tab.Screen name="Pesquisar" component={PesquisarScreen} />
</Tab.Navigator>
);
// you could use Tabs directly instead of AppStack but there can be more requirements...
const AppStack = () => (
<Stack.Navigator>
<Stack.Screen name="Tabs" component="Tabs" />
</Stack.Navigator>
);
Basically, you can pass Navigators instead of Screens as component prop. And you need to use AppStack inside NavigationContainer. You need to think carefully before implementing the navigators because hierarchy is important as it states here.
Related
I have a navigation container that calls a function to my stack. I would like to navigate to a tab screen from a stack screen AND change a state in that tab screen. I have given some code below and a demo. In the demo you can see on screen3(stack screen) I am trying to navigate to Home(tab screen) and change a state so that it renders the MapHome screen.
I am unsure how to pass the state to the bottom tab screen without rendering it elsewhere.
I appreciate any insight more than you know.
here is my demo as well as some code below of App.js. You must run the demo in IOS or android, it will not work for web.
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { MaterialCommunityIcons } from '#expo/vector-icons';
import Home from './Home'
import ListHome from './screens/screen1'
import MapHome from './screens/screen2'
import Screen3 from './screens/screen3'
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Stack.Navigator
initialRouteName="Home">
<Stack.Screen name="Home" component= {Home} options={{headerShown: false}}/>
<Stack.Screen name="Screen1" component= {ListHome} options={{headerShown: false}}/>
<Stack.Screen name="Screen2" component= {MapHome} options={{headerShown: false}}/>
</Stack.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
initialRouteName="Home"
screenOptions={{
tabBarActiveTintColor: '#F60081',
tabBarInactiveTintColor: '#4d4d4d',
tabBarStyle: {
backgroundColor: '#d1cfcf',
borderTopColor: 'transparent',
},
}}
>
<Tab.Screen
name="Home"
component={MyTabs}
options={{
tabBarLabel: 'Home',
headerShown: false,
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="home" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Screen3"
component={Screen3}
options={{
tabBarLabel: 'Screen3',
headerShown: false,
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="account-group" color={color} size={size} />
),
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
const Stack = createStackNavigator();
You can pass a parameter from Screen3 to the Home screen.
I have forked your demo and did some small refactoring (including changing from Class to Function components just for preference) so you may need to adapt it to your needs. You can find it here.
In Screen3 you can modify your onPress logic to the following:
navigation.navigate('Home', {
screen: 'Home',
params: {
componentToShow: 'Screen2'
}
});
Here's a breakdown of why you are seeing Home twice:
navigation.navigate('Home', { // <-- Tab.Screen name.
screen: 'Home', // <-- Stack.Screen name.
params: {
componentToShow: 'Screen2'
}
});
With this code you are now passing a route.param to the Home screen. I've included a useEffect to run any time route.params changes.
const [whichComponentToShow, setComponentToShow] = React.useState("Screen1");
React.useEffect(() => {
if(route.params && route.params.componentToShow) {
setComponentToShow(route.params.componentToShow);
}
}, [route.params]);
Now, whenever a User navigates to the Home screen and a componentToShow route parameter is provided, the Home screen will update.
My code is currently stuck in the top of the home screen.
However, I would like to let you go to the home screen when I press the login button on the login screen.
I hope there is no Top Navigation or Bottom Tab Navigation on the login screen here.
Current Homescreen
Below is my code.
Login is an import of screen parts.
Please help me.
app.js
export default function App() {
return (
<AuthContext.Provider value={authContext}>
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'ios-home' : 'ios-home-outline';
} else if (route.name === 'Purchase') {
iconName = focused ? 'ios-list-circle' : 'ios-list';
} else if (route.name === 'Wish List') {
iconName = focused ? 'ios-star' : 'ios-star-outline'
}
// You can return any component that you like here!
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
activeTintColor: '#32cd32',
inactiveTintColor: 'gray',
}}
>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Wish List" component={WishListStackScreen} />
<Tab.Screen name="Purchase" component={PurchaseStackScreen} />
</Tab.Navigator>
</NavigationContainer>
</AuthContext.Provider>
);
};
I believe you should first make a stack navigator in app.js with the login screen and the homepage in the stack:
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Home" component={Home} />
</Stack.Navigator>
</NavigationContainer>
When you press the button on the login screen you should do this when the user is authenticated:
<Button onPress={() => {navigation.navigate('Home')}}
Then on the homepage you should add the tabnavigator
Im following a react native app tutorial that has stack navigation only but I'm trying to also add nested navigation so I have a tab bar at the bottom that leads to additional pages I'm building but can't seem to figure out how to implement in my current code. Here is my code so far not sure if i'm on the right track or way off
{
DashBoard: DashBoardScreen,
Articles: ArticlesScreen,
Article: ArticleScreen,
Search: SearchScreen
},
{
initialRouteName: 'DashBoard',
defaultNavigationOptions: ({navigation}) => ({
headerStyle, headerTitleStyle,
headerRight: (<SearchBtn onPress={() => navigation.navigate('Search')}/>)
})
}
);
const Tab = createBottomTabNavigator();
const TabNavigator = () => (
<Tab.Navigator>
<Tab.Screen name="News" component={DashBoard}/>
<Tab.Screen name="Game" component={Game}/>
</Tab.Navigator>
)```
Here's how you can achieve this:
const BottomTab = createBottomTabNavigator();
const Stack = createStackNavigator();
const AppStack = () => {
return (
<BottomTab.Navigator>
<BottomTab.Screen name="Screen1" component={Screen1} />
<BottomTab.Screen name="Screen2" component={Screen2} />
</BottomTab.Navigator>
);
};
const MainStack = () => {
return (
<Stack.Navigator headerMode="none">
<Stack.Screen name="App" component={AppStack} />
<Stack.Screen name="Auth" component={AuthStack} />
</Stack.Navigator>
);
};
I have created a navigator with 4 tab navigator screens->Home, Search, Upload and Library; and I have stack navigation screens like Sign up, log in, home tabs, and video.
Now, I want to remove the bottom tab bar from the Upload screen but am not sure whether it is possible?
Below is the exact code that I have written:
import React from 'react';
import 'react-native-gesture-handler';
import {StatusBar} from 'react-native';
import {NavigationContainer, DarkTheme} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {createMaterialBottomTabNavigator} from '#react-navigation/material-bottom-tabs';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialIcons';
import Home from '../screens/Home';
import Search from '../screens/Search';
import Library from '../screens/Library';
import ProfileAuthor from '../screens/ProffileAuthor';
import Shoot from '../screens/Shoot';
import LoginScreen from '../screens/LoginScreen';
import SignUp from '../screens/SignUp';
const Stack = createStackNavigator();
const Tab = createMaterialBottomTabNavigator();
function myTabs() {
return (
<Tab.Navigator
initialRouteName="Home"
activeColor="#000"
inactiveColor="#000"
barStyle={{
backgroundColor: '#FFF',
}}>
<Tab.Screen
name="Home"
component={Home}
options={{
title: 'Home',
tabBarLabel: 'Home',
tabBarIcon: ({color}) => (
<MaterialCommunityIcons color={color} name="home" size={26} />
),
}}
/>
<Tab.Screen
name="Search"
component={Search}
options={{
title: 'Search',
tabBarLabel: 'Search',
tabBarIcon: ({color}) => (
<MaterialCommunityIcons color={color} name="search" size={26} />
),
}}
/>
<Tab.Screen
name="Upload"
component={Shoot}
options={{
title: 'Upload',
tabBarLabel: 'Upload',
tabBarIcon: ({color}) => (
<MaterialCommunityIcons color={color} name="add-box" size={26} />
),
}}
/>
<Tab.Screen
name="Library"
component={Library}
options={{
title: 'Library',
tabBarLabel: 'Library',
tabBarIcon: ({color}) => (
<MaterialCommunityIcons
color={color}
name="person-outline"
size={26}
/>
),
}}
/>
</Tab.Navigator>
);
}
const Routes = () => {
return (
<NavigationContainer theme={DarkTheme}>
<Stack.Navigator>
<Stack.Screen
name="login"
component={LoginScreen}
options={{
header: () => null,
}}
/>
<Stack.Screen
name="signup"
component={SignUp}
options={{
header: () => null,
}}
/>
<Stack.Screen
name="Home"
component={myTabs}
options={{
header: () => null,
}}
/>
<Stack.Screen
name="Video"
component={ProfileAuthor}
options={{
header: () => null,
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
export default Routes;
could anyone please tell me whether it is possible in this structure, if yes then how to implement? any help would be great.
tabBarVisible: false
It is working when you used createBottomTabNavigator from #react-navigation/bottom-tabs. but if you will use createMaterialBottomTabNavigator from #react-navigation/material-bottom-tabs to create bottom tabs, it's not working. createMaterialBottomTabNavigator can't hide the tab bar, you can see that there is no such option in docs. You should try to nest the bottom-tabs navigator inside the stack navigator if you want it to disappear on the other screens of the stack navigator. The below code is for the hide tab bar in createBottomTabNavigator.
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const BottomTabs = createBottomTabNavigator();
<BottomTabs.Navigator>
<BottomTabs.Screen name={AppRoute.MORE} component={MoreStack}
options={({ route }) => ({
tabBarVisible: getTabBarVisibility(route),
tabBarLabel: languages.stack_More
})}
/>
</BottomTabs.Navigator>
const getTabBarVisibility = (route: any) => {
const routeName = route.state ? route.state.routes[route.state.index].name: '';
if (routeName === AppRoute.PROFILE
|| routeName === AppRoute.HELP_CENTER
|| routeName === AppRoute.TERMS_CONDITION) {
return false;
}
return true;
}
I have created a nested tab navigator in my stack navigator as follows:
const Stack = createStackNavigator()
const Tab = createBottomTabNavigator();
function TabNav () {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen}></Tab.Screen>
<Tab.Screen name="Category" component={CategoryScreen}></Tab.Screen>
<Tab.Screen name="Settings" component={SettingsScreen}></Tab.Screen>
</Tab.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Tab" component={TabNav}></Stack.Screen>
<Stack.Screen name="Category" component={CategoryScreen}>
</Stack.Screen>
<Stack.Screen
name="SpecialBeerScreen" component= {SpecialBeerScreen}>
</Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
)
}
However, the header now looks like Tab for each of the screens. How do I override this Header for each of the screens with a custom text, such as Home, Category etc.
Update: This is still valid in react-navigation v6
Use getFocusedRouteNameFromRoute as outlined in the react-navigation docs in the section Setting parent screen options based on child navigator's state to access information of screens in nested navigators.
Change this line
<Stack.Screen name="Tab" component={TabNav}></Stack.Screen>
to the following (adding the options prop):
<Stack.Screen
name="Tab"
component={TabNav}
options={({ route }) => {
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';
switch (routeName) {
case 'Category': {
return {
headerTitle: 'Category',
};
}
case 'Settings': {
return {
headerTitle: 'Settings',
};
}
case 'Home':
default: {
return {
headerTitle: 'Home',
};
}
}
}}
/>
You also have to import getFocusedRouteNameFromRoute, so also add:
import { getFocusedRouteNameFromRoute } from '#react-navigation/native';
If I understand your problem correctly, you wanted to change the stack title when the tab is changed. In this case, you may use React Context to control it.
(I also put this code in snack https://snack.expo.io/#gie3d/change-header-from-tab)
edit: 1, I separated it into 3 files and assume that it's all in the same directory.
HomeTitleContext.js
export const HomeTitleContext = React.createContext({
title: 'default title',
setTitle: () => {},
});
App.js
import { HomeTitleContext } from './HomeTitleContext';
export default function App() {
const [title, setTitle] = useState('default title');
return (
<HomeTitleContext.Provider
value={{
title,
setTitle,
}}
>
<HomeTitleContext.Consumer>
{(ctx) => (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Tab"
component={TabNav}
options={{ title: ctx.title }} // The title will be dynamically changed here
/>
<Stack.Screen
name="Category"
component={OtherScreen}
></Stack.Screen>
<Stack.Screen
name="SpecialBeerScreen"
component={OtherScreen}
></Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
)}
</HomeTitleContext.Consumer>
</HomeTitleContext.Provider>
);
}
In your component, for example: HomeScreen, you set up a useFocusEffect and change the title from setTitle which you'll get from the context
HomeScreen.js
import React, { useContext } from 'react';
import { useFocusEffect } from '#react-navigation/native';
import { View, Text } from 'react-native';
import { HomeTitleContext } from './HomeTitleContext';
const HomeScreen = ({ navigation }) => {
const { setTitle } = useContext(HomeTitleContext);
useFocusEffect(() => {
setTitle('this is home');
});
return (
<View>
<Text>Home</Text>
</View>
);
};