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>
);
}
Related
I'm experiencing a display issue with React Native / React Navigation where navigating to a different tab within a Tab Navigator causes a bad bounce animation to trigger when the tab opens. This issue only happens after logging in, and after this happens once or twice it doesn't happen again.
Below is a 8 second video clip of the issue:
Youtube clip of error
What I've tried so far:
InteractionManager.runAfterInteractions within componentDidMount() to prevent fetching data during navigation animation
Turning lazyLoad on & off within the TabNavigator
Forcing the mapview to reload with this.forceUpdate() before navigating to another tab
Unfortunately none of this has worked, and I'm not sure where the problem is coming from now.
What I'm running:
"react-navigation": "^1.5.11"
"expo": "^26.0.0"
Relevant code snippet of React Navigation setup (the clip shows navigating from the userInfo > map > yourEvents:
export default class App extends React.Component {
render() {
const Stack = {
FirstView: {
screen: TabNavigator(
{
map: {
screen: HomeMapScreen,
transitionConfig: () => fromLeft(),
navigationOptions: ({ navigation }) => ({
header: null
})
},
yourEvents: {
screen: YourEventsScreen,
transitionConfig: () => fromLeft(),
navigationOptions: ({ navigation }) => ({
header: null
})
},
...
},
{
navigationOptions: {
animationEnabled: "false"
},
tabBarPosition: "bottom",
animationEnabled: false,
swipeEnabled: false,
tabBarOptions: {
showIcon: "true", // Shows an icon for both iOS and Android
style: {
backgroundColor: "#04151F"
},
showLabel: false,
activeTintColor: "#59C9A5",
inactiveTintColor: "#F7FFF7",
labelStyle: {
fontSize: 10
},
iconSize: Platform.OS === "ios" ? 30 : 24
}
}
)
},
...
userInfo: {
screen: UserInfo,
transitionConfig: () => fromLeft(),
navigationOptions: {
drawerLabel: <Hidden />
}
},
...
};
const DrawerRoutes = {
...
Home: {
name: "Home",
screen: StackNavigator(Stack, {
initialRouteName: "FirstView",
transitionConfig: () => fromLeft(),
headerMode: "none",
drawerIcon: () => {
return <Icon name="map" type="foundation" size={30} color={tintColor} />;
}
})
},
SecondViewStack: {
name: "SecondViewStack",
screen: StackNavigator(Stack, {
initialRouteName: "SecondView",
transitionConfig: () => fromLeft(),
icon: () => {
return <Icon name="map" type="foundation" size={30} color={tintColor} />;
}
})
}
};
const RootNavigator = StackNavigator(
{
Drawer: {
name: "Drawer",
screen: DrawerNavigator(DrawerRoutes, {
drawerBackgroundColor: "#D8DDEF",
transitionConfig: () => fromLeft(),
contentComponent: DrawerContent,
contentOptions: {
backgroundColor: 'black',
flex: 1
},
})
},
...Stack
},
{
headerMode: "none"
}
);
return (
<Provider store={store}>
<View style={styles.container}>
<MyStatusBar translucent backgroundColor="#04151F" barStyle="light-content" />
<RootNavigator />
</View>
</Provider>
);
}
}
Snippet of component displaying the "bounce" issue when loaded:
class YourEventsScreen extends Component {
state = {
attendingEvents: true,
ownedEvents: false,
isLogoutVisible: false,
animatePressAttend: new Animated.Value(1),
animatePressHost: new Animated.Value(1),
didFinishInitialAnimation: false,
}
static navigationOptions = {
title: "Your Events",
headerLeft: null,
tabBarIcon: ({ tintColor }) => {
return <Icon name="calendar" size={30} color={tintColor} type="entypo"/>;
}
};
componentDidMount() {
InteractionManager.runAfterInteractions(() => {
this.props.fetchOwnedEvents(this.props.currentUser.userId);
this.props.fetchAttendingEvents(this.props.currentUser.attendingEvents);
this.setState({
...this.state,
didFinishInitialAnimation: true,
});
});
}
Any ideas or insight on the issue here is very much appreciated!
I can only imagine what is happening here is that you are using LayoutAnimation somewhere when this tab change takes place. A good strategy when encountering issues like this is to remove code until the issue is resolved, then add it back piece by piece.
I had a similar issue when I toggled the visibility of a MapView by updating the state with react-navigation's onDidFocus and onWillBlur functions (from NavigationEvents).
Once I stopped updating this and removed my showMap parameter, the bouncing effect went away.
I'm not getting params to my header in main(homeStack) screen.
When I change createBottomTabNavigator with Home: {screen: Home}, not with Home: {screen: homeStack} than parameters are transferred to my header successfully in that screen. I don't know why this happens. PLease help me to get that user data to my header in Home screen.
Also, I want to have a different header in each of the tabs and I want to display data to header via params, which I put in login as:
navigation.navigate('Home', { user: user });
My app.js code:
const homeStack = createStackNavigator({
screen1: {
screen: Home,
navigationOptions: {
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
},
},
},{ initialRouteName: 'screen1'});
const Routes = createStackNavigator({
Login: {
screen: Login,
navigationOptions: {
header: null,
}
},
Main: {
screen: createBottomTabNavigator({
Home: { screen: homeStack },
Profile: { screen: Settings },
}, {
initialRouteName:'Home',
})
}
},{
initialRouteName: 'Login',
});
export default class App extends React.Component {
render() {
return (
<Routes />
);
}
}
Code in screen home where is header:
export default class Home extends React.Component {
static navigationOptions = ({ navigation, navigationOptions }) => {
console.log(navigationOptions);
console.log(navigation);
// Notice the logs ^
// sometimes we call with the default navigationOptions and other times
// we call this with the previous navigationOptions that were returned from
// this very function
/*return {
title: navigation.getParam('user', 'A Nested Details Screen'),
headerStyle: {
backgroundColor: navigationOptions.headerTintColor,
},
headerTintColor: navigationOptions.headerStyle.backgroundColor,
headerTitle: navigation.state.params.user.name,
}; */
return {
headerTitle: navigation.state.params.user.name,
}
};
render() {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Text>Changes you make will automatically reload.</Text>
<Text>Shake your phone to open the developer menu.</Text>
</View>
);
}
}
I have 2 screens.
First one is Login screen(without header) where I have "createStackNavigation"
and second one is Main screen where I would like to have bottom navigation(2 tabs) with Header in each of these two tabs.
I did bottom navigation but custom header does not work at all...It's only basic header. Can anyone have some basic example of this? Or can help me with tutorial?
My code in app.js:
const RootStack = createStackNavigator(
{
Home: {
screen: Login,
navigationOptions: ({ navigation }) => ({
header: null,
}),
},
Main: MainScreen,
},
{
initialRouteName: 'Home',
headerMode: 'screen',
}
);
My code in "Main" screen:
export default createBottomTabNavigator(
{
Domov: {
screen: HomeScreen,
},
Dnevnik: Diary,
},
{
navigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, tintColor }) => {
const { routeName } = navigation.state;
let iconName;
if (routeName === 'Domov') {
iconName='home';
} else if (routeName === 'Dnevnik') {
iconName='ios-calendar';
}
return (
<Icon name={iconName} style={{fontSize: 20, color: '#FFF'}} />
);
},
}),
tabBarPosition: 'bottom',
tabBarOptions: {
activeTintColor: 'white',
showLabel: false,
inactiveTintColor: '#4C2601',
style: {
backgroundColor: '#033D51',
},
},
});
Thank you
Add header: null in your navigationOptions in Main screen. You'll be able to add your custom header.
I adding image of screen, this work in part of screen.
The Contacts screen need to be main page and not screen1 but its didn't work if i replace between them.
I adding the code, in 'LogedInNavigator' have there TabNavigator and DrawerNavigator - the 'Contants' page initializing from TabNavigator and part two - Screen1 with the side menu it's from DrawerNavigator - maybe it's doing the problem?
LogedInNavigator.js
import.......
styles......
const LoggedInNavigator = TabNavigator(
{
Contacts: {screen: ContactScreen,},
Chat: {screen: ChatScreen,},
Dashbaord: {screen: DashbaordScreen,},
Profile: {screen: ProfileScreen,},
Search: {screen: SearchScreen,},
},
{
initialRouteName: "Contacts",
tabBarPosition: "bottom",
tabBarOptions: {
showIcon: true,
activeTintColor: 'white',
}
}
);
export default () => <LoggedInNavigator onNavigationStateChange={null} />
export const Drawer = DrawerNavigator ({
Home:{
screen: Screen1,
navigationOptions: {
drawer:{
label: 'Home',
},
}
},
Camera: {
screen: Screen2,
navigationOptions: {
drawer:{
label: 'Camera',
},
}
},
})
Contants.js
class Contacts extends Component {
componentDidMount() {
// TBD loggedin should come from login process and removed from here
const { loggedIn, getContacts } = this.props;
loggedIn(1);
getContacts();
}
render() {
const Router = createRouter( () => ({})); //IDAN
const { navigation, avatar, contacts } = this.props;
return (
<NavigationProvider router={Router}>
<View style={{flex:1}}>
<ContactView
navigation={navigation}
avatar={avatar}
contacts={contacts}
/>
<Drawer />
</View>
</NavigationProvider>
);
}
}
const mapStateToProps = (state) => {
return (
{
avatar: state.user.user.avatar,
contacts: state.contacts.contacts,
}
);
};
export default connect(mapStateToProps, { loggedIn, getContacts })(Contacts);
Help me please..
After a while, i want to answer on my own question (with react-navigation v2)
everything inside <RootNavigator/>
const RootNavigator= createDrawerNavigator({ Tabs }, {
contentComponent: SideMenu,
drawerWidth: Dimensions.get('window').width * .75,
})
SideMenu:
class SideMenu extends Component {
render() {
return ( //...your side menu view )
}
}
Tab:
export default createBottomTabNavigator({
Menu: {
screen: HomeStack,
navigationOptions: {
title: 'תפריט',
tabBarIcon: ({ focused, tintColor }) => {
return <Icon name={'home'} size={20} color={tintColor} />;
},
}
},
Dashboard: {
screen: DashboardStack,
navigationOptions: {
title: 'בית',
tabBarOnPress: ({ navigation, defaultHandler }) => handleTabPress(navigation, defaultHandler),
tabBarIcon: ({ focused, tintColor }) => {
return <Icon name={'dashboard'} size={20} color={'green'} />;
},
}
},
QuickView: {
screen: QuickNav,
navigationOptions: {
title: 'מבט מהיר',
tabBarIcon: ({ focused, tintColor }) => {
return <Icon name={'short-list'} size={20} color={tintColor} />;
},
},
},
Chat: {
screen: Chat,
navigationOptions: {
title: "צ'אט",
tabBarIcon: ({ focused, tintColor }) => {
return <Icon name={'chat'} size={20} color={tintColor} />;
},
},
},
},
{
initialRouteName: 'Dashboard',
tabBarOptions: {
activeTintColor: 'green',
labelStyle: {
fontSize: 16,
marginBottom: 3,
},
},
},
)
For v5 onwards you can use drawer style
import deviceInfoModule from 'react-native-device-info';
<Drawer.Navigator
drawerStyle={{
width: deviceInfoModule.isTablet()
? Dimensions.get('window').width * 0.55
: Dimensions.get('window').width * 0.7,
}}
You can set the drawer width using Dimensions width. See the docs here
https://reactnavigation.org/docs/navigators/drawer
import { Dimensions } from 'react-native';
...
const { width } = Dimensions.get('screen');
...
export const Drawer = DrawerNavigator (
{
Home:{
screen: Screen1,
navigationOptions: {
drawer:{
label: 'Home',
},
}
},
Camera: {
screen: Screen2,
navigationOptions: {
drawer:{
label: 'Camera',
},
}
},
},
{
drawerWidth: width
});
In react-navigation version 6, you can use the drawerStyle in the screenOptions prop in the Drawer.Navigator component to change the width and add styles. This applies the applied style to all screens in the navigator.
<Drawer.Navigator
screenOptions: {{
drawerStyle: {
width: 240
}
}}
>
If you want the drawer to cover the entire screen, then import Dimensions from the react-native library and use Dimensions.get('window').width
import { Dimensions } from 'react-native'
<Drawer.Navigator
screenOptions: {{
drawerStyle: {
width: Dimensions.get('window').width
}
}}
>
Refer to react-navigation drawer for more.
Hye..
I currently playing around react-navigation and trying to solve issue where header did not hide when Drawer open...
I hope anyone can share how to solve this buggy header..below I attached my code integration of DrawerNavigator inside StackNavigator.
const Home = DrawerNavigator({
HomeMenu: { screen: HomeMenu },
Messages: { screen: Messages },
Notifications: { screen: Notifications },
Badges: { screen: Badges },
Leaderboard: { screen: Leaderboard },
Profile: { screen: Profile },
Logout: { screen: Logout }
});
const MainActivity = StackNavigator({
Home: { screen: Home }
})
Thank you in advance!
You can hide header like:-
const Home = DrawerNavigator({
HomeMenu: { screen: HomeMenu,
navigationOptions: {
header:false, //hide header if not needed so whole screen slide
},
},
Messages: { screen: Messages },
Notifications: { screen: Notifications },
Badges: { screen: Badges },
Leaderboard: { screen: Leaderboard },
Profile: { screen: Profile },
Logout: { screen: Logout }
});
const MainActivity = StackNavigator({
Home: { screen: Home }
})
Use StackNavigator inside DrawerNavigator and set headerMode: 'none' to root StackNavigator
const MenuStackNavigator = StackNavigator({
Dashboard: {
screen: Dashboard,
navigationOptions: {
title: 'Dashboard',
}
},
});
const PagesStackNavigator = StackNavigator({...});
const DrawerNavigator = DrawerNavigator({
MenuStack: {
screen: MenuStackNavigator,
navigationOptions: {
drawer: () => ({
label: 'MenuStackNavigator',
})
},
},
Pages: {
screen: PagesStackNavigator,
navigationOptions: {
drawer: () => ({
label: 'PagesStackNavigator',
})
},
}
});
const AppNavigator = StackNavigator({
Drawer: { screen: DrawerNavigator },
}, {
headerMode: 'none',
});
I had the same issue (TabNav nested inside DrawerNav) and found a super easy fix I wanted to share! This issue is discussed here, in the repo for react-navigation. The fix that I implemented (on the screen you want to hide):
MAIN: {
screen: MainTabs,
navigationOptions: {
drawerLabel: () => null // to hide this header
},
},
Slightly different version:
static navigationOptions = {
drawerLabel: () => null
}
Hope this helps!
I found the ways, I just need to do the other way around..
Make DrawerNavigator as the root Navigator and put StackNavigator inside it.. then there will be no header when open the drawer
I was also playing around this problem and found out that StackNavigator has to be nested inside Drawer one, but there are a lot of issues about this solution like synchronizing active menu state inside drawer and card stack.
// Manifest of possible screens
const MenuButton = ({ navigation }) => (
<View>
<TouchableOpacity onPress={() => navigation.navigate('DrawerOpen')}>
<Icon name="bars" style={{color: 'white', padding: 10, marginLeft:10, fontSize: 20}}/>
</TouchableOpacity>
</View>
);
const Nav = StackNavigator({
StoriesScreen: {
screen: StoriesScreen,
navigationOptions: { title: 'Stories' }
},
LaunchScreen: { screen: LaunchScreen },
LoginScreen: {
screen: LoginScreen,
navigationOptions: { title: 'Login' }
}
}, {
navigationOptions: {
header: navigation => ({
style: styles.header,
left: <MenuButton navigation={navigation} />,
}),
}
})
const PrimaryNav = DrawerNavigator({
StoriesScreen: {
screen: Nav,
navigationOptions: { title: 'Stories' }
},
LaunchScreen: { screen: LaunchScreen },
LoginScreen: {
screen: LoginScreen,
navigationOptions: { title: 'Login' }
}
})
I too had the same issue and solved by add "headerMode":none in main StackNavigator.
Example:
const AppMainStack = DrawerNavigator({
ActivitiesScreen: {screen: ActivitiesScreen},
}, {
drawerPosition: 'right',
});
const AppNavigator = StackNavigator({
StartScreen: {screen: StartScreen},
EnterCodeScreen: {screen: EnterCodeScreen},
CreateAccountScreen: {screen: CreateAccountScreen},
ProfileSetupScreen: {screen: ProfileSetupScreen},
SignInScreen: {screen: AppMainStack},
}, {
headerMode: 'none',
});