I was trying to open a modal from bottomnavigator , was following this tutorial - https://snack.expo.io/SyJKMkFUM
I am using react-navigation -3.0.9
here is my app.js
class App extends React.Component {
renderItem = (route, index) => {
const {
navigation,
jumpToIndex,
} = this.props;
const isCapture = route.routeName === 'Capture';
const focused = index === navigation.state.index;
return (
<TouchableWithoutFeedback
key={route.key}
onPress={() => isCapture ? this.props.navigation.navigate('CaptureModal') : jumpToIndex(index)}
>
<View >
<Text >{route.routeName}</Text>
</View>
</TouchableWithoutFeedback>
);
};
render() {
const {
navigation,
} = this.props;
const {
routes,} = navigation.state;
return (
<View style={styles.container}>
{routes && routes.map(this.renderItem)}
</View>
);}}
const Screen = (props) => (
<View >
<Text>{props.title} Screen</Text>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
const TabNavigator = createBottomTabNavigator({
Home: {
screen: HomeScreen
},
Link: {
screen: LinksScreen
},
settings: {
screen: View,
},
});
const CaptureStack = createStackNavigator({
Capture: {
screen: (props) => <Screen title="Capture" {...props} />,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Capture',
headerLeft: (
<Button
title="Back"
onPress={() => navigation.goBack(null)}
/>),}),},})
const RootStack1 = createStackNavigator({
Main: {
screen: TabNavigator,
},
CaptureModal: {
screen: CaptureStack,
navigationOptions: {
gesturesEnabled: false,
},
},
}, {
headerMode: 'none',
mode: 'modal',
});
const AppNavigator = createAppContainer(RootStack1);
export default AppNavigator;
Could anyone please explain what is wrong with this ? Could it be a routing version issue as the tutorial is using 1.0.0 .
I did solve my issue if anyone is lokking here how i achieved it -
My Goal ->
Click on Main Screen -> Tabbed Activity -> on click of any tab new screen
in my app.js i created navigation as follows -
const TabNavigator = createBottomTabNavigator({
HomeScreen: {
screen: HomeScreen
},
LinksScreen: {
screen: LinksScreen
},
LoginScreen: {
screen: SurveyScreen,
},
},
{
initialRouteName : 'HomeScreen' ,
});
const RootStack1 = createStackNavigator({
TabNavigator: {
screen: TabNavigator,
},
CaptureModal: {
screen: LoginScreen,
navigationOptions: {
gesturesEnabled: false,
},
},
}, {
headerMode: 'none',
mode: 'modal',
headerLeft: null
});
const mainStack = createStackNavigator({
InsideApp: {
screen: MainScreen,
},
StartScreen: {
screen: RootStack1,
},
} ,{
headerMode: 'none',
headerLeft: null
},);
const AppNavigator = createAppContainer(mainStack);
export default AppNavigator;
Now My Modal screen will pop up on click of third tab (SurveyScreen) , to achieve that inside surveyscreen all i had to do was override didmount function and open modal from there -
componentDidMount() {
console.log('didmount called.');
this.props.navigation.navigate('CaptureModal')
}
For back navigation from modal to tab activity i used stackreset -
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'TabNavigator' })],
});
and click of back -
onPress={ () => this.props.navigation.dispatch(resetAction)
Related
I created top navigator in my android react native project :
const topSurvayorsNavigator = createMaterialTopTabNavigator({
ActiveSurveyor: {
screen: MomayezanScreen,
params: { status: 1 },
navigationOptions: {
tabBarLabel: <Text style={{ fontSize: 12, fontFamily: 'IranianSans' }}>ممیزان فعال</Text>,
tabBarIcon: (tabInfo => {
return (
<Icon name="stars" size={15} color={tabInfo.tintColor} />
);
})
},
},
DeActiveSurveyor: {
screen: MomayezanScreen,
params: { status: 0 },
navigationOptions: {
tabBarLabel: <Text style={{ fontSize: 12, fontFamily: 'IranianSans' }}>ممیزان غیرفعال</Text>,
tabBarIcon: (tabInfo => {
return (
<Icon name="md-stars" size={15} color={tabInfo.tintColor} />
);
})
},
}
}, {
swipeEnabled: true,
tabBarOptions: {
labelStyle: {
fontSize: 12
},
activeTintColor: Colors.darkGray
}
});
After that i added topSurvayorsNavigator to main stack navigator :
const AuditMomayezanNavigator = createStackNavigator({
Dashboard: DashboardScreen,
ListSurveyor: {
screen: topSurvayorsNavigator,
},
Detail: SurveyorDetailsScreen,
}, {
defaultNavigationOptions: defaultNavOptions
});
and finally :
export default createAppContainer(AuditMomayezanNavigator);
In MomayezanScreen according params i loaded different data on flatlist. In page i want to set custom header so i do this:
MomayezanScreen.navigationOptions = ({ navigation }) => {
const statusId = navigation.getParam('status');
console.log(statusId);
return {
header: () => <CustomHeader
title={`ممیزان ${statusId === 0 ? 'غیر فعال' : 'فعال'}`} />
}
}
But unfortunately nothing is changed and header did not have any title.
Actually custom header is not set in header component! What is your idea?
I changed MomayezanScreen.navigationOptions to this:
MomayezanScreen.navigationOptions = ({ navigation }) => {
const statusId = navigation.getParam('status');
console.log(statusId);
return {
headerTitle: `test`
}
}
But still nothing is printed on header.
You're setting the header in MomayezanScreen which is inside createMaterialTopTabNavigator. You can only set the header in a screen which is inside createStackNavigator.
This is explained in detail in React Navigation docs: https://reactnavigation.org/docs/en/navigation-options-resolution.html
I am trying to change the Header Title to an image, but I can't get it to work. What I have tried is use the LogoTitle class to render an image instead of a title in my Top Tab Navigator, hiding the header works and rendering a title works also:
TopNavigator.navigationOptions = {
headerTitle:'test'
};
And then I tried to change it to an image:
TopNavigator.navigationOptions = () =>{
headerTitle:<LogoTitle/>
};
I use the latest Expo SDK
This is my full code:
import React from 'react';
import { createStackNavigator, createMaterialTopTabNavigator } from 'react-navigation';
import TabBarIcon from '../components/TabBarIcon';
import PartyScreen from '../screens/PartyScreen';
import EventScreen from '../screens/EventScreen';
import FestivalScreen from '../screens/FestivalScreen';
import ActivityScreen from '../screens/ActivityScreen';
import TestScreen from '../screens/TestScreen';
class LogoTitle extends React.Component {
render() {
return (
<Image
source={require('../assets/images/Header_Logo.png')}
style={{ width: '100%', height: '100%', resizeMode: 'center', backgroundColor: 'black' }}
/>
);
}
}
const PartyStack = createStackNavigator({
Party: PartyScreen,
});
PartyStack.navigationOptions = {
tabBarLabel: "Partys",
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
name={
'md-calendar'
}
/>
),
};
const EventStack = createStackNavigator({
Event: EventScreen,
});
EventStack.navigationOptions = {
tabBarLabel: 'Events',
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
name={
'md-calendar'
}
/>
),
};
const FestivalStack = createStackNavigator({
Festival: FestivalScreen,
});
FestivalStack.navigationOptions = {
tabBarLabel: 'Festivals',
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
name={
'md-calendar'
}
/>
),
};
const ActivityStack = createStackNavigator({
Activity: ActivityScreen,
});
ActivityScreen.navigationOptions = {
tabBarLabel: 'Activiteit',
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
name={
'md-calendar'
}
/>
),
};
const TestStack = createStackNavigator({
Test: TestScreen,
});
TestScreen.navigationOptions = {
tabBarLabel: 'Test',
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
name={
'md-calendar'
}
/>
),
};
const TopNavigator = createMaterialTopTabNavigator({
PartyStack,
EventStack,
FestivalStack,
ActivityStack,
TestStack
}, {
tabBarOptions: {
activeTintColor: '#5B71F9',
inactiveTintColor: '#888888',
showIcon: false,
labelStyle: {
fontSize: 14
},
scrollEnabled : true,
style: {
backgroundColor: '#fff',
shadowColor: '#fff',
shadowOffset: {
width: 0,
height: 0,
},
shadowOpacity: 0,
shadowRadius: 0,
elevation: 0,
height: 47,
borderBottomWidth: 1,
borderBottomColor: '#E8E8E8'
},
indicatorStyle: {
height: 2,
backgroundColor: '#5B71F9'
},
},
}, navigationOptions = {
header:{visible:false}
});
TopNavigator.navigationOptions = {
headerTitle:<LogoTitle/>
};
export default TopNavigator;
I don't get why it does render Text but images wont, I guess it has to do with the brackets
try this:
const RouteConfigs = {
// Your routes
};
const StackNavigatorConfig = {
navigationOptions: {
header: (navigation) => ({
title: ( <LogoTitle navigation={navigation} /> )
})
},
};
export default createStackNavigator(RouteConfigs, StackNavigatorConfig);
I have a small app which should have a drawer menu in the side
and a small Plus button which slides up a panel with more 4 buttons
something like this:
the app.js initiate a Switch navigator that have 2 screens, one for login and one for a Stack
the stack has the DrawerNavigation as the menu screen and other screens also.
and in the Drawer navigation there's some other screens:
the code below:
const SideMenuDrawer = createDrawerNavigator({
Main: MainScreen,
'Invite A Friend Screen': InviteAFriendScreen,
About: AboutScreen,
Schedule: ScheduleScreen,
Groups: GroupsScreen
},
{
navigationOptions: ({ navigation }) => {
const { routeName } = navigation.state.routes[navigation.state.index];
return {
headerTitle: routeName,
headerTintColor: '#ffffff',
headerStyle: {
backgroundColor: '#2F95D6',
borderBottomColor: '#ffffff',
borderBottomWidth: 3,
},
headerTitleStyle: {
fontSize: 18
}
}
}
}
);
const AppStack = createStackNavigator({
MainScreen: SideMenuDrawer,
PhotoScreen: PhotoScreen,
DocumentScreen: DocumentScreen,
AudioScreen: AudioScreen,
GalleryScreen: GalleryScreen
},
{
defaultNavigationOptions: ({ navigation }) => {
return {
headerLeft: (
<Icon
name="md-menu"
size={35}
style={{ paddingLeft: 10 }}
color="white"
onPress={() => navigation.openDrawer()}
/>
)
}
}
}
);
const AppContainer = createAppContainer(createSwitchNavigator(
{
LoginSplashScreen: LoginSplashScreen,
MainScreen: AppStack
},
{
initialRouteName: 'MainScreen',
}
));
export default class App extends React.Component {
render() {
return (
<AppContainer />
)
}
}
and the SlidingPanel is in the render() function of the Menu screen:
class MainScreen extends React.Component {
state = {
slideUpPanelvisible: false
}
render() {
const window = Dimensions.get('window');
const { navigation } = this.props;
const signedIn = global.signedIn;
const name = global.name;
const photoUrl = global.photoUrl;
return (
<View style={{ flex: 1 }}>
<View style={[styles.container]} >
<Text style={styles.header}>Welcome {name}</Text>
<Image style={styles.image} source={{ uri: photoUrl }} />
</View>
<View style={styles.addButton}>
<TouchableOpacity
onPress={() => this.setState({ slideUpPanelvisible: true })} >
<Icon name="add-circle" size={60} color='#0E2E49' />
</TouchableOpacity>
</View>
<SlidingUpPanel
visible={this.state.slideUpPanelvisible}
startCollapsed={true}
draggableRange={{ top: window.height * 0.4, bottom: 0 }}
onRequestClose={() => this.setState({ slideUpPanelvisible: false })}
allowDragging={true}
>
<View style={styles.panelcontainer}>
<Text style={styles.header}>Text inside the Sliding Panel </Text>
</View>
</SlidingUpPanel>
</View>
)
}
}
but the drawer doesnt show on android device (GEnymotion, android studio and a real device)
on an Iphone device it works great!
what am i doing wrong?
In latest versions of react-navigation, you need to add react-native-gesture-handler and link it. Otherwise gestures will not work on Android.
https://github.com/kmagiera/react-native-gesture-handler
npm install --save react-native-gesture-handler
react-native link react-native-gesture-handler
Also make sure things are linked properly(Sometimes auto-linking can fail). After this you can see that drawer is working in android.
Refer Documentation: https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html
I am new to react native and i am trying to make screen which has two tabs and one drawer. For this i am using createTabnavigator inside createDrawerNavigator and this createDrawerNavigator inside createStackNavigator. I have enabled the swipeEnable but still the swipe gesture is not working for drawer but the tabs are working fine. Please help me to find a way to make this work.
Here is the set up of createDrawernavigator.
App.js
import React from "react";
import { TouchableOpacity } from "react-native";
import { connect } from "react-redux";
import {
createDrawerNavigator,
createStackNavigator,
createAppContainer,
createMaterialTopTabNavigator
} from "react-navigation";
import Icon from "react-native-vector-icons/MaterialIcons";
import { DrawerActions } from "react-navigation-drawer";
import Login from "./modules/LoginPage/components/Form";
import NewComplaint from "./modules/newComplaint/components/Form";
import OpenTab from "./modules/homePage/components/OpenTab";
import ClosedTab from "./modules/homePage/components/CloseTab";
import complaintDetails from
"./modules/ComplaintDetail/components/Details";
const App = props =>
// eslint-disable-next-line react/prop-types
props.authentication.data.success ? (
<LoginRootStack />
) : (
<LoggedOutRootStack />
);
const Drawer = createDrawerNavigator(
{
OpenComplaints: createMaterialTopTabNavigator(
{
Open: { screen: OpenTab },
Closed: { screen: ClosedTab }
},
{
order: ["Open", "Closed"],
initialRouteName: "Open"
}
),
ClosedComplaints: createMaterialTopTabNavigator(
{
Open: { screen: OpenTab },
Closed: { screen: ClosedTab }
},
{
order: ["Open", "Closed"],
initialRouteName: "Closed"
}
)
},
{
navigationOptions: ({ navigation }) => ({
title: "Home",
drawerLockMode: "unlocked",
headerLeft: (
<TouchableOpacity
style={{ marginLeft: 10 }}
onPress={() => {
navigation.dispatch(DrawerActions.toggleDrawer());
}}
>
<Icon name="menu" size={30} color="#fff" navigation={navigation}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
style={{ marginRight: 10 }}
onPress={() => {
navigation.navigate("newComplaint");
}}
>
<Icon name="add" size={30} color="#fff" navigation={navigation} />
</TouchableOpacity>
),
headerStyle: {
backgroundColor: "#2980b9"
},
headerTintColor: "#fff",
headerTitleStyle: {
fontWeight: "bold"
}
}),
swipeEnabled: true,
contentOptions: {
activeTintColor: "#2980b9"
}
}
);
const LoggedOutStack = createStackNavigator(
{
Login: { screen: Login },
Home: { screen: Drawer },
newComplaint: { screen: NewComplaint },
Details: { screen: complaintDetails }
},
{
swipeEnabled: true,
initialRouteName: "Login",
headerMode: "none"
}
);
const LogggedInStack = createStackNavigator(
{
Login: { screen: Login },
Home: { screen: Drawer },
newComplaint: { screen: NewComplaint },
Details: { screen: complaintDetails }
},
{
swipeEnabled: true,
initialRouteName: "Home"
}
);
function mapStateToProps(state) {
return {
authentication: state.authentication
};
}
export const LoginRootStack = createAppContainer(LogggedInStack);
export const LoggedOutRootStack = createAppContainer(LoggedOutStack);
export default connect(mapStateToProps)(App);
Put LoggedOutStack and LogggedInStack in switchStackNavigator and put that switchStackNavigator in createAppContainer.
I'm using a TabNavigator nested inside DrawerNavigator.
My TabNavigator contains 2 screens and DrawerNavigator has 4 routes, one of which is the TabNavigator.
When I swipe to second tab inside my TabNavigator, then use the drawer to go to some other route and use the drawer to come back to TabNavigator, it is an error.
Here is the TabNavigator:
const MyTabNavigator = TabNavigator(
{
Tab1: {
screen: StackNavigator1,
navigationOptions: ({ navigation }) => ({
tabBarLabel: "Tab1"
})
},
Tab2: {
screen: StackNavigator2,
navigationOptions: ({ navigation }) => ({
tabBarLabel: "Tab2",
header: false
})
}
},
{
tabBarPosition: 'top',
tabBarOptions: {
activeTintColor: '#000000',
inactiveTintColor: '#707070',
labelStyle: labelStyle,
style: {
backgroundColor: '#ffffff',
},
indicatorStyle: {
borderBottomColor: '#ff3278',
borderBottomWidth: 3
}
}
});
And here is the DrawerNavigator:
const MyDrawerNavigator = DrawerNavigator(
{
Tabs: {
screen: MyTabNavigator
},
Key1: {
screen: Navigator1
}
.
.
.
},
{
contentComponent: (props) => {
return <View>
<View style={styles.drawerHeaderStyle}>
<Text style={styles.drawerHeaderTextStyle}>{`Welcome user`}</Text>
</View>
<DrawerItems {...props} />
<View style={styles.emptySpace} />
<Touchable
onPress={() => {
// Logout User
}}
style={styles.logoutButton}
background={Touchable.Ripple('grey')}>
<Text style={styles.buttonFont}>{"Logout"}</Text>
</Touchable>
</View>
}
});
Each of the StackNavigators have 2 screens. Something like:
const StackNavigator1 = StackNavigator(
{
Screen1: {
screen: Screen1,
navigationOptions: ({ navigation }) => ({
header: false
})
},
Screen2: {
screen: Screen2,
navigationOptions: ({ navigation }) => ({
header: false,
tabBarVisible: false,
swipeEnabled: false,
drawerLockMode: 'locked-closed'
}),
}
}, {
headerMode: "screen"
});
So when I swipe to "Key1" then use drawer to come to Navigator1 and finally use drawer to go back to "Tabs", I get an error saying
Error: There is no route defined for key Screen1, Must be one of Screen3, Screen4.
Where Screen3 and Screen4 reside inside StackNavigator2.
I hope I was able to describe the issue appropriately. Any ideas?
Alright. I've figured out a solution. It's a little hard to explain but I'll try nonetheless.
To get it to work I had to overwrite the onItemPress method of the DrawerItems myself.
My DrawerNavigation now looks like this:
const MyDrawerNavigator = DrawerNavigator(
{
Tabs: {
screen: MyTabNavigator
},
Key1: {
screen: Navigator1
}
.
.
.
},
{
contentComponent: (props) => {
return <View>
<View style={styles.drawerHeaderStyle}>
<Text style={styles.drawerHeaderTextStyle}>{`Welcome user`}</Text>
</View>
<DrawerItems {...props} onItemPress={(routeOptions) => {
props.navigation.navigate(routeOptions.route.routes[routeOptions.route.index].routeName, {})
}} />
<View style={styles.emptySpace} />
<Touchable
onPress={() => {
// Logout User
}}
style={styles.logoutButton}
background={Touchable.Ripple('grey')}>
<Text style={styles.buttonFont}>{"Logout"}</Text>
</Touchable>
</View>
}
});
Notice the onItemPress added in DrawerItems. This rather looks like a bug in react-navigation itself.
I had been facing these problems too but I came up with a solution building up my own header that will call drawer navigator.
class Header extends Component {
render() {
return (
<View>
<Logo />
<TouchableOpacity onPress={() => this.props.navigation.navigate('DrawerOpen')}>
<Icon size={24} style={{ color: '#fff' }} name="navicon" />
</TouchableOpacity>
</View>
)
}
}
Header.propTypes = {
navigation: PropTypes.instanceOf(Object).isRequired,
}
export default withNavigation(Header)
Wrapping your screens with withNavigation() maybe will do the trick.