Scenario :
I have an app working with three tabs for navigation (School, Admin, Family);
I am now trying to add in other screens, that will not have corresponding tabs. These screens will be navigated to using something like this.props.navigation.navigate('ChatScreen')
Issue
- With my past solution, any time I added a screen it would add a tab for that screen.
Todo :
I would like to have the tabs in my navigation stack, as well as other normal (not-tab) screens.
I would like both the tabs and the header to be persistent
I have been unsuccessful at combining both, and have tried many
variations of the code below.
Tried Code :
const School = createStackNavigator({
School: {
screen: SchoolScreen,
navigationOptions: {
headerTitle: <CustomHeaderTitle screen='school'/>
}
}
});
const Admin = createStackNavigator(
{ Admin: {
screen: AdminScreen,
navigationOptions: {
headerTitle: <CustomHeaderTitle screen='admin' />
}
}
});
const Family = createStackNavigator(
{
Family: {
screen: FamilyScreen,
navigationOptions: {
headerLeft: null,
headerTitle: <CustomHeaderTitle screen='family' />
}
}
}
);
const ChatStack = createStackNavigator({
CreateChat: CreateChatScreen
});
const TabStack = createBottomTabNavigator(
{
School: School,
Admin: Admin,
Family: Family
},
{
navigationOptions: ({ navigation }) => ({
tabBarIcon: () => {
const { routeName } = navigation.state;
return <Image id={1} source={require('./app/img/school_logo.png')} />
},
tabBarLabel: navigation.state.routeName
}),
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
style: {
backgroundColor: 'black',
height: 55
}
}
}
);
const RootStack = createStackNavigator({
Root: ChatStack,
Tabs: TabStack
})
export default class App extends Component {
render() { return (
<Provider store={store}>
<RootStack />
</Provider>
);
}
}
I apologize, I cannot get this code to format after fighting with it for some time.
Thank you for any help or recommendations in advance!
Please suggest.
Credit to an unnamed redditor:
You'll have to nest you're entire stack into each screen of the tab navigator. As far as I know you can't access different screens in a StackNavigator if they are nested inside a different TabNavigator screen.
For example, if you want to be able to navigate to the chat screen from the SchoolScreen, you'll have to include that component inside your School navigator.
const School = createStackNavigation({
School: {
screen: SchoolScreen
},
SchoolChat: {
screen: CreateChatScreen
}
});
From there your main TabNavigator should look about the same
const TabStack = createBottomTabNavigator({
School: School
});
you should hide the RootStack header when TabStack is focused
TabStack.navigationOptions = {
// Hide the header from root stack
header: null,
};
and you did not need add stack to CreateChatScreen
const RootStack = createStackNavigator({
Tabs: TabStack,
ChatScreen: CreateChatScreen,
})
In react native navigation 5
import React from 'react';
import {Text} from 'react-native';
import {createStackNavigator} from '#react-navigation/stack';
import {NavigationContainer} from '#react-navigation/native';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
function Scr(){
return <Text>hello</Text>;
}
function MyTabs() {
return (
<Tab.Navigator
initialRouteName="Expolre"
tabBarOptions={{
activeTintColor: '#414757',
}}>
<Tab.Screen name="Expolre" component={Scr} />
</Tab.Navigator>
);
}
export default function Routing() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="login"
component={Scr}
options={{header: () => null}}
/>
<Stack.Screen
name="dashboard"
component={MyTabs}
options={{header: () => null}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Related
Anyone here who knows a cleaner way then ยด{ navigation }:{navigation: NativeStackNavigationProp}` in Typescript in a React Native cli project? I already tried many different things from stackoverflow but they didn't work.
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import {createNativeStackNavigator, NativeStackNavigationProp} from '#react-navigation/native-stack';
import {Button, Text} from "react-native";
const Stack = createNativeStackNavigator();
export const App = () => {
return (
<MyStack/>
);
};
const MyStack = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Welcome' }}
/>
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
const HomeScreen = ({ navigation }:{navigation: NativeStackNavigationProp<any>}) => {
return (
<Button
title="Go to Jane's profile"
onPress={() =>
navigation.navigate('Profile', { name: 'Jane' })
}
/>
);
};
const ProfileScreen = ({ navigation, route }:{navigation: NativeStackNavigationProp<any>, route: any}) => {
return <Text>This is {route.params.name}'s profile!</Text>;
};
Note : Please note that you need to use React.FC to type check your functional components.
If you refer to React Navigation's guide you will see that the correct way of type checking your navigation is:
Type check your stack first
type RootStackParamList = {
Home: { title: string };
Profile: undefined;
};
const Stack = createNativeStackNavigator<RootStackParamList>();
Type check your screens
type Props = NativeStackScreenProps<RootStackParamList, 'Home'>;
const HomeScreen:React.FC<{navigation:Props['navigation']}> = ({ navigation }) => {
// code
}
or
type Props = NativeStackScreenProps<RootStackParamList, 'Home'>
const HomeScreen = () => {
// code
const navigation = useNavigation<Props['navigation']>()
}
You can use either of the two ways, I always use the hook, since it feels more clean to me.
I am trying to make dynamic tab.screen.
my code is like this:
import React from 'react';
import { Text, View, TouchableOpacity, Modal } from 'react-native';
import AsyncStorage from '#react-native-community/async-storage';
import Icon from 'react-native-vector-icons/FontAwesome5';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import Home from '../mainscreen/GeneralScreen';
import Core from '../mainscreen/CoreScreen';
import Docs from '../mainscreen/GeneralScreen';
import ESS from '../mainscreen/CoreScreen';
import General from '../mainscreen/GeneralScreen';
import HR from '../mainscreen/CoreScreen';
import Payroll from '../mainscreen/GeneralScreen';
import Server from '../mainscreen/CoreScreen';
const Tab = createBottomTabNavigator();
const tabcomponents = {
"Home" : Home,
"Core" : Core,
"Docs" : Docs,
"ESS" : ESS,
"General" : General,
"HR" : HR,
"Payroll" : Payroll,
"Server" : Server
};
class TabNavigator extends React.Component {
constructor() {
super();
this.state = {
dashboardtab:[],
}
this.tabnavigatorasync();
}
tabnavigatorasync = async () => {
try {
const dashboardtab = await AsyncStorage.getItem('dashboardtab');
const dashboardtabParse = JSON.parse(dashboardtab);
this.setState({dashboardtab: dashboardtabParse});
} catch (error) {
}
}
render(){
const tabnavigatorRender = this.state.dashboardtab.map((item, index) => {
const tabcomponentsrender = tabcomponents[item.admintab.label];
return <Tab.Screen name={item.admintab.label} component={tabcomponentsrender} key={index}/>
});
return(
<Tab.Navigator>
{tabnavigatorRender}
</Tab.Navigator>
)
}
}
export default TabNavigator;
the result appears an error like this:
Error: Couldn't find any screens for the navigator. Have you defined any screens as its children?
is there something wrong with the code i made?
As the error states you are not having any screens inside the TabNavigator.
When the component is mounted the array is empty and the data is loaded later.
So you can fix this like below
render(){
const tabnavigatorRender = this.state.dashboardtab.map((item, index) => {
const tabcomponentsrender = tabcomponents[item.admintab.label];
return <Tab.Screen name={item.admintab.label} component={tabcomponentsrender} key={index}/>
});
// Add this to return null or you can also show <ActivityIndicator/>
if(this.state.dashboardtab.length===0)
return null;
return(
<Tab.Navigator>
{tabnavigatorRender}
</Tab.Navigator>
)
}
Also call the tabnavigatorasync from componentDidMount instead of calling it from the constructor.
componentDidMount(){
this.tabnavigatorasync();
}
<NavigationContainer>
<Tab.Navigator
screenOptions={{
headerShown: false,
tabBarActiveTintColor: '#A968EE',
tabBarStyle: {
position: 'absolute',
backgroundColor: 'black',
},
}}>
{data?.map((val, index) => (
<Tab.Screen
key={index}
name={val?.Name}
options={{
tabBarLabel: val?.Name,
tabBarIcon: () => (
<Image
style={{
height: 20,
width: 20,
}}
source={val?.ImageSource}
/>
),
}}
component={val?.Component}
/>
))}
</Tab.Navigator>
</NavigationContainer>
Current code
App.js
import React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import { Icon } from 'react-native-elements';
import HomeScreen from 'app/src/screens/home/Index';
import DetailScreen from 'app/src/screens/home/Detail';
import MypageScreen from 'app/src/screens/mypage/Index';
import InitialScreen from 'app/src/screens/authentication/Initial';
const Home = {
screen: HomeScreen,
navigationOptions: ({ navigation }) => {
return {
title: 'Home',
};
},
};
const Detail = {
screen: DetailScreen,
navigationOptions: ({ navigation }) => {
return {
title: 'Detail',
};
},
};
const Mypage = {
screen: MypageScreen,
navigationOptions: ({ navigation }) => {
return {
title: 'MyPage',
};
},
};
const Initial = {
screen: InitialScreen,
navigationOptions: ({ navigation }) => {
return {
title: 'Initial',
};
},
}
const HomeStack = createStackNavigator(
{
Home,
Detail,
},
{
initialRouteName: 'Home',
navigationOptions: {
tabBarIcon: <Icon name="home" />,
},
}
);
const MypageStack = createStackNavigator(
{
Mypage,
},
{
initialRouteName: 'Mypage',
navigationOptions: {
tabBarIcon: <Icon name="person" />,
},
}
);
const postLoginNavigator = createBottomTabNavigator({
Home: HomeStack,
Mypage: MypageStack,
});
const AppNavigator = createStackNavigator({
Initial,
PostLogin: postLoginNavigator
},{
mode: 'modal',
headerMode: 'none',
initialRouteName: 'Initial'
})
const AppContainer = createAppContainer(AppNavigator);
export default class App extends React.Component {
render() {
return (
<AppContainer />
);
}
}
What I want to do
I wanna make tabs in bottom using createBottomTabNavigator.
Home and My Page tabs.
Error that I'm facing
Error: Creating a navigator doesn't take an argument. Maybe you are trying to use React Navigation 4 API with React Navigation 5?
ps
I'm using
"#react-navigation/native": "^5.2.3",
"#react-navigation/stack": "^5.2.8",
"#react-navigation/bottom-tabs": "^5.0.6",
I would appreciate it if you could give me any advices.
const MyTabs = () => {
return(
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Mypage" component={MypageScreen} />
</Tab.Navigator>);
}
Can you try this? I think I missed the return statement
Add the bottomTabNavigator inside a StackNavigator. In future, if you are adding more screens you can add it to the stack
import React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { Icon } from 'react-native-elements';
import HomeScreen from 'app/src/screens/home/Index';
import MypageScreen from 'app/src/screens/mypage/Index';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const MyTabs = () => {
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Mypage" component={MypageScreen} />
</Tab.Navigator>
}
const AuthStack = () => (
<Stack.Navigator>
<Stack.Screen name="Tabs" component={MyTabs} />
</Stack.Navigator>
);
const AuthenticationNavigator = () => (
<NavigationContainer>
<AuthStack />
</NavigationContainer>
);
export default AuthenticationNavigator;
I have been using react-navigation for a while now. When I tried referring to this Snack on Expo, I realised that if I navigate inside a tab, then I could not navigate back to Home Tab screen by pressing the Home button on the tabbar. I have to click on Back button that is present on the screen in order to navigate back to home.
Here is a piece of code:
const HomeStack = StackNavigator({
Home: { screen: HomeScreen },
Details: { screen: DetailsScreen },
});
const SettingsStack = StackNavigator({
Settings: { screen: SettingsScreen },
Details: { screen: DetailsScreen },
});
export default TabNavigator({
Home: { screen: HomeStack },
Settings: { screen: SettingsStack },
});
Referring to above code, if I click on Settings from the tabbar and then navigate to Details present inside that stack then I cant navigate back to Settings when I click on Settings again. I have to click on Back button that is present in the top section of the screen.
What is wrong here?
It would be a good idea to use unique names for every new route. React Navigation uses these names as unique keys to differentiate between routes. I see a Settings in your default TabNavigator, and also another Settings for your SettingsStack StackNavigator. Same for Details too. (Simply renaming might solve your issue too, not sure).
So taking your example (and renaming Settings to SettingsScreen),
const HomeStack = StackNavigator({
Home: { screen: HomeScreen },
Details: { screen: DetailsScreen },
});
const SettingsStack = StackNavigator({
SettingsScreen: { screen: SettingsScreen },
Details: { screen: DetailsScreen },
});
export default TabNavigator({
Home: { screen: HomeStack },
Settings: { screen: SettingsStack },
});
Now, to go back to SettingsScreen from Settings > Details, you might wanna try
dispatch(NavigationActions.navigate({
routeName: 'Settings',
action: NavigationActions.navigate({ routeName: 'SettingsScreen' })
}))
The idea is that in case of nested navigators, if you want to go back to another screen via the parent, you should call Navigations.navigate twice, in a nested manner.
It's in their docs somewhere. I'll try adding the link here for reference as soon as I find it.
The tab navigator buttons only switch between the shown views. Since you navigated within your stack navigator, that's what you're seeing.
If you want to add the functionality that the stack is reset every time the tab button is pressed, you can do that by providing your own tab component and then calling reset on the stack navigator.
createTopTabs = () => {
return(
<MaterialTopTabs.Navigator initialRouteName="Tab_Daily"
tabBarOptions={{
showIcon: true,
style: { backgroundColor: '#C4e672' },
labelStyle: { fontSize: 12, fontWeight: 'bold' },
/* tabStyle: { width: 100 }, */
tabStyle: { height: 50 },
}}>
<MaterialTopTabs.Screen
name="Tab_ToDoNote"
component={TabToDoNote}
options={
{
title: '',
/* tabBarLabel: "Daily", */
tabBarIcon: () =>
(
<Icons_SimpleLine
style={
[
{
color: 'red',
}
]
}
size={25}
name={'note'}
/>
)
}
}
/>
<MaterialTopTabs.Screen
name="Tab_Daily"
component={TabDaily}
options={
{
title: '',
tabBarLabel: "Daily",
tabBarIcon: () =>
(
<Icons_MaterialCommunity
style={
[
{
color: 'red'
}
]
}
size={18}
name={'calendar-today'}
/>
)
}
}
/>
<MaterialTopTabs.Screen
name="Tab_Monthly"
component={TabMonthly}
options={
{
tabBarLabel: "Monthly",
tabBarIcon: () =>
(
<Icons_MaterialCommunity
style={
[
{
color: 'red'
}
]
}
size={18}
name={'calendar-month-outline'}
/>
)
}
}
/>
<MaterialTopTabs.Screen
name="Tab_Yearly"
component={TabYearly}
options={
{
tabBarLabel: "Yearly",
tabBarIcon: () =>
(
<Icons_MaterialCommunity
style={
[
{
color: 'red'
}
]
}
size={18}
name={'calendar-multiple'}
/>
)
}
}
/>
</MaterialTopTabs.Navigator>
);
}
I've just set up a React Navigation TabNavigator which works fine but I'm having issues with transitions between my views.
I get the following error message from trying to redirect the user to another page after a click on a button.
Cannot read property 'navigate' of undefined
I'm super confused on how to make this work.
Here is the navigator:
import React from 'react';
import { TabNavigator, StackNavigator } from 'react-navigation';
import ElemList from './src/components/ElemList';
import ElemShow from './src/components/ElemShow';
const RootTabs = TabNavigator({
Home: {
screen: ElemList,
navigationOptions: {
tabBarLabel: 'Home',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-home' : 'ios-home-outline'}
size={26}
style={{ color: tintColor }}
/>
),
},
main: {
screen: StackNavigator({
show: { screen: ElemShow },
})
}
}
});
Here is my code:
onRowPress(data) {
this.props.navigation.navigate('ElemShow', {id: data})
};
render() {
const { key, elem } = this.props;
return (
<TouchableWithoutFeedback onPress={() => this.onRowPress(elem)}>
<View style={styles.viewStyle} key={key}>
<Text>Here is the info</Text>
</View>
</TouchableWithoutFeedback>
)
}
The problem is with this line
this.props.navigation
Should be
this.props.navigator
Additionally I don't know that navigate is part of the screens API; typically you will use push or resetTo.