I'm having some issue with react-navigation. I want to have a custom Tab Bar in which one of the screen is made of a StackNavigator.
So Basically, I have
Tab
-- StackNavigator
-- Dashboard (main)
-- Wallet
-- Etc...
-- Other tabs...
To make my custom tab, I used TabRouter, createNavigationContainer and createNavigator
const Tabs = TabRouter(
{
Main: {
screen: Main,
},
},
{
initialRouteName: 'Main',
tabBarComponent: TabView.TabBarBottom,
swipeEnabled: false,
animationEnabled: false,
backBehavior: 'none',
},
);
const CustomTabView = ({ router, navigation }) => {
const { routes, index } = navigation.state;
const ActiveScreen = router.getComponentForState(navigation.state);
return (
<View style={{ flex: 1 }}>
<ActiveScreen
navigation={addNavigationHelpers({
...navigation,
state: routes[index],
})}
/>
<View style={styles.tabContainer}>
{routes.map(route => (
<TouchableOpacity // custom tab items
key={route.routeName}
onPress={() => navigation.navigate(route.routeName)}
>
<TabItem />
</TouchableOpacity>
))}
</View>
</View>
);
};
const TabBar = createNavigationContainer(createNavigator(Tabs)(CustomTabView));
Where Main is:
const Main = StackNavigator({
Dashboard: Dashboard,
Wallet: Wallet,
...
}, { initialRouteName: 'Dashboard' });
But it doesn't because Dashboard must declares a screen.
What I'm doing wrong? I don't know. How can we put a StackNavigator in a TabBar screen?
Fortunately, it's very easy.
What StackNavigator (or any other navigator) returns is React component. That means you can use it as screen component for any other route.
Thus, one of your routes screen will be StackNavigator.
Sample code
const Tabs = TabNavigator(
{
Main: {
screen: StackNavigator({
Dashboard: {
screen: Dashboard,
}
Wallet: {
screen: Wallet,
}
}),
},
},
{
initialRouteName: 'Main',
tabBarComponent: TabView.TabBarBottom,
swipeEnabled: false,
animationEnabled: false,
backBehavior: 'none',
},
);
Related
I would like my initial page to NOT contain a header. However, I would like a header to appear on each subsequent page. Right now my current stackNavigator looks like this:
const AppNavigator = createStackNavigator(
{
HomeScreen: HomePage,
SecondPage: SecondPage,
Account: Account,
About: About,
},
{
initialRouteName: 'HomeScreen',
headerMode: 'none',
navigationOptions: {
headerVisible: false,
},
},
);
export default createAppContainer(AppNavigator);
Here is the basic boilerplate for my second page:
const SecondPage: () => React$Node = () => {
return (
<>
<StatusBar barStyle="dark-content" />
<View style={styles.body}>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>This is the Secondpage</Text>
</View>
</View>
</>
);
};
export default SecondPage;
You have three ways of doing this using navigationOptions:
1) Directly when you define your navigator:
createStackNavigator({
{
HomeScreen: {
screen : HomePage,
navigationOptions: {header : null}
},
SecondPage: SecondPage,
Account: Account,
About: About,
},
{ ... } //your navigationOptions
})
2) Directly inside the screen as said by Shashank Malviya. Only if you aren't using a functional component.
3) Return defaultNavigationOptions as a function and use navigation state:
createStackNavigator({
{ ... }, //your RouteConfigs
{
initialRouteName: 'HomeScreen',
headerMode: 'none',
defaultNavigationOptions : ({navigation}) => navigation.state.routeName === "HomePage" && {header: null}
},
})
Can be written on every component before render
static navigationOptions = ({ navigation }) => {
const { state } = navigation
return {
headerVisible: true // can be true or false
}
}
I created 2 pages namely Home and Profile and added these two pages to createDrawerNavigation. And then added this drawer navigation to a stack navigator. Now when I try to display the title on the header of the current page. I'm unable to do it.
Here is my code.
Tried DefaultNavigationOptions and got the props value of navigator but doesn't work that way.
const MenuStack = createDrawerNavigator(
{
HomePage: {
screen: Home
},
ProfilePage: {
screen: Profile
}
},
{
drawerWidth: Math.round(Dimensions.get("window").width) * 0.75,
contentOptions: {
activeTintColor: theme.colors.accent
}
}
);
const AppStack = createStackNavigator(
{
MenuStack: {
screen: MenuStack
}
},
{
defaultNavigationOptions: ({ navigation }) => {
return {
headerLeft: (
<Icon
style={{ paddingLeft: 10 }}
onPress={() => navigation.toggleDrawer()}
name="bars"
size={30}
/>
),
headerRight: (
<Icon
style={{ paddingRight: 10 }}
onPress={() => navigation.toggleDrawer()}
name="search"
size={30}
/>
),
headerTitle: navigation.state.routeName,
headerStyle: {
backgroundColor: theme.colors.accent
}
};
}
}
);
export const createRootNavigator = (signedIn = false) => {
return createAppContainer(
createSwitchNavigator(
{
SignedIn: {
screen: AppStack
},
SignedOut: {
screen: WelcomeStack
},
SignInAndOut: {
screen: AuthStack
}
},
{
initialRouteName: signedIn ? "SignedIn" : "SignedOut"
}
)
);
};
I added a headerTitle property in the createStackNavigator but I tell the "menustack" as the title for every page.
Expected the title of the page on the header.
You have 2 options:
1. Render your own header component in each screen
You can render a header inside your screens, either a custom one or one from a component library such as react native paper.
function Home() {
return (
<React.Fragment>
<MyCustomHeader title="Home" />
{{ /* screen content */ }}
</React.Fragment>
);
}
2. Create a stack navigator for each screen that needs to have header
const MenuStack = createDrawerNavigator({
HomePage: {
screen: createStackNavigator({ Home })
},
ProfilePage: {
screen: createStackNavigator({ Profile })
}
});
Keep in mind that while this requires less code, it's also less performant.
I'm trying to add a custom menu icon button inside the navigation header in my react native app. I successfully did that, but the press event doesn't fire as long as the icon is in the header. For example, if I have the icon here:
https://www.dropbox.com/s/xyah9ei43wgt1ut/menu_regular.png?dl=0
The press event doesn't work, but if I have it here (moved it lower):
https://www.dropbox.com/s/54utpr1efb3o0lm/menu_moved.png?dl=0
The event fires ok.
Here's my current setup navigator:
const MainNavigator = createStackNavigator(
{
login: { screen: MainLoginScreen },
signup: { screen: SignupScreen },
profileScreen: { screen: ProfileScreen },
main: {
screen: createDrawerNavigator({
Home: createStackNavigator(
{
map: {
screen: MapScreen,
headerMode: 'screen',
navigationOptions: {
headerVisible: true,
headerTransparent: false,
headerLeft: (
<View style={{ position: 'absolute', left: 10, display: 'flex', zIndex: 11550 }}>
<Icon
raised
name='bars'
type='font-awesome'
color='rgba(255, 255, 255, 0)'
reverseColor='#444'
onPress={() => { console.log("press"); navigation.goBack() }}
reverse
/>
</View>
)
}
},
history: { screen: HistoryScreen },
foundItem: { screen: FoundItemScreen },
}
),
Settings: {
screen: SettingsScreen,
navigationOptions: ({ navigation }) => ({
title: 'Edit Profile',
})
}
}, {
contentComponent: customDrawerComponent,
drawerWidth: width * 0.8
}
)
}
}, {
headerMode: 'screen',
navigationOptions: {
headerTransparent: true,
headerLeftContainerStyle: { paddingLeft: 20 }
}
}
);
The icon from the screenshot is inside headerLeft.
I've also tried various zIndex values, but with no luck.
Thanks in advance!
Edit:
The drawer has the same issue on the first item, press events don't work on the full area of the drawer item when it's over the header:
https://www.dropbox.com/s/krva5cgp7s59d13/drawer_opened.png?dl=0
Try to wrap Icon inside some Touchable Element like TouchableOpacity/TouchableHighlight and then add an onPress prop to that element
You can get the onPress event in navigationOption's header using navigation params like
static navigationOptions = ({ navigation }) => ({
headerLeft: (
<TouchableOpacity onPress={() => navigation.state.params.handleSave()} >
<Image source={ImageSource} />
</TouchableOpacity>
)
})
Then set the navigation params from the componentDidMount like:
componentDidMount() {
this.props.navigation.setParams({ handleSave: this.onSavePress.bind(this)})
}
then can call the method like:
onSavePress() {
// Do Something
}
i am switching from react navigator to react-navigation and i am actually fine with this approach, but struggeling with one issue.
I want to use a drawer navigation and a bottom aligned Tab-Navigation.
This part is working as expected - no issues here.
I want to have the tabbed navigation fixed with 3 Buttons that will have the same action all over the app. ( eg. dashboard / search / favourites )
From Dashboard you can navigate one level deeper. As i am doing it now, the Label of the Tab formerly "Dashboard" changes to the Name of the navigated-to Item.
to clarify, i added a stack-navigation in the Dashboard-Screen-Tab, so the user can navigate through that pages.
How can i prevent the tabs' labes and actions to change while navigating within the tabs' stack?
Basically i want a fixed Tab Navigation on each screen.
Should i create a fixed View-Component to achieve that?
Here is my setup:
App.js
const MetaTabNavigator = TabNavigator({
Dashboard: {
screen: MachineNavigator
},
Search: { screen: SearchScreen },
Favourites: { screen: FavouritesScreen },
},
{
tabBarPosition: Platform.OS === "ios" ? 'bottom' : 'top',
animationEnabled: true,
tabBarOptions: {
activeTintColor: STYLES.HIGHLIGHT_COLOR
},
swipeEnabled: false,
backBehavior: 'none'
});
const MetaDrawerNavigator = DrawerNavigator({
Home: {
screen: MetaTabNavigator,
navigationOptions: {
drawer: {
label: 'Drawer',
icon: ({ tintColor }) => <Icon name="rocket" size={24} />
},
},
}
},
{
contentComponent: props => <Menu {...props} />
}
);
AppRegistry.registerComponent('myApp', () => MetaDrawerNavigator);
MachineNavigator
const MachineNavigator = StackNavigator({
Main: {
screen: MachineOverview,
navigationOptions: ({ navigation }) => ({
title: "Dashboard",
headerLeft: (
<TouchableOpacity onPress={() => navigation.navigate("DrawerOpen")}>
<IOSIcon name="ios-menu" size={30} />
</TouchableOpacity>
),
headerStyle: { paddingRight: 10, paddingLeft: 10 }
})
},
Category: {
screen: Category,
navigationOptions: (props) => ({
title: "Kategorie",
})
},
ItemDetail: {
screen: ItemDetail,
navigationOptions: (props) => ({
title: "Video",
})
}
})
export default MachineNavigator;
According to this issue, you can add to the tab configuration the tabBarLabel property to control the label:
const MetaTabNavigator = TabNavigator({
Dashboard: {
screen: MachineNavigator, navigationOptions: {tabBarLabel: 'Dashboard'}
},
...
I made myself a custom TabBar with gradient background and stuff with TabRouter (because you can't make one with TabNavigator right?). But one of my tab is a StackNavigator and it doesn't display the navBar on top of the screen. Do I need to manually add it the CustomTabView?
Shouldn't the ActiveScreen handle the navBar if it's a StackNavigator?
const Tabs = TabRouter(
{
Main: {
// My StackNavigator which should have a navBar on top...
screen: StackNavigator({
Dashboard: {
screen: Dashboard,
title: 'Dashboard',
},
Wallet: { screen: Wallet },
Deck: { screen: Deck },
EnterpriseProfile: { screen: EnterpriseProfile },
}, {
initialRouteName: 'Dashboard',
}),
},
},
{
initialRouteName: 'Main',
swipeEnabled: false,
animationEnabled: false,
backBehavior: 'none',
},
);
const CustomTabView = ({ router, navigation }) => {
const { routes, index } = navigation.state;
const ActiveScreen = router.getComponentForState(navigation.state);
return (
<View style={{ flex: 1, paddingTop: Platform.OS === 'ios' ? 20 : 0 }}>
<ActiveScreen
navigation={addNavigationHelpers({
...navigation,
state: routes[index],
})}
/>
// My Custom TabBar because I don't want the default one from TabNavigator
<View style={styles.tabContainer}>
{routes.map(route => (
<TouchableOpacity key={route.routeName} onPress={() => navigation.dispatch({ type: 'Navigation/BACK' })}
>
<TabItem />
</TouchableOpacity>
))}
</View>
</View>
);
};
const TabBar = createNavigationContainer(createNavigator(Tabs)(CustomTabView));
export default TabBar;