Route not found when nesting TabNavigator inside DrawerNavigator - android

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.

Related

React Native: custom header not set to header navigation

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

Open a modal on BottomTabNavigator click react-native

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)

rn-sliding-up-panel not working when inside a DrawerNavigation in Android- React Native

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

React-Native: Navigate to drawer within Stack navigator

My app currently uses a drawer navigator as its main navigation.
One of the drawer screens is a StackNavigator. The first screen within this nested StackNavigator contains a button which should direct the user to a DrawerNavigator screen.
How can I have a button within my StackNavigator screen "Home" that navigates to its parent DrawerNavigator screen "Logs"?
Stack Navigator (Home is component with button that should direct to Drawer screen 'Logs'):
const Stack = createStackNavigator({
Home: {
screen: Home,
navigationOptions: { header: null }
},
Settings: {
screen: Settings,
navigationOptions: { header: null }
}
});
Stack with header:
class StackWithHeader extends Component {
render() {
return (
<Container>
<Head title="Consultation" drawerOpen={() => this.props.navigation.dispatch(DrawerActions.openDrawer())} />
<Stack/>
</Container>
)
}
}
Drawer Navigator with nested Stack Navigator:
const Drawer = createDrawerNavigator(
{
Head: {
screen: StackWithHeader,
navigationOptions: ({ navigation }) => ({
title: "Head",
headerLeft: <Icon name="menu" style={{ paddingLeft: 10 }} onPress={() => this.props.navigation.dispatch(DrawerActions.openDrawer())} />,
})
},
Logs: {
screen: Logs,
navigationOptions: ({ navigation }) => ({
title: "Logs",
headerLeft: <Icon name="menu" style={{ paddingLeft: 10 }} onPress={() => this.props.navigation.dispatch(DrawerActions.openDrawer())} />,
})
},
}
);
Using NavigationActions may help you in this case
import { NavigationActions } from 'react-navigation';
...
_onPress = () => {
this.props.navigation.dispatch(
NavigationActions.navigate({
routeName: 'Logs',
params: {},
// optional, you can navigate to sub-route of Logs here by
// action: NavigationActions.navigate({ routeName: 'SubRoute' }),
})
)
}
By the way, I recommend you to integrate redux with react-navigation to simply navigate. Refer here

React-Navigation - DrawerNavigator Performance issue

I'm experiencing a performance issue with DrawerNavigator with the following code.
class App extends Component {
componentDidMount(){
var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync("StrToHash", salt);
}
render() {
console.log("rendering");
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit App.js
</Text>
<Button
onPress={ () => {} }
title="Learn More"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>
</View>
);
}
}
Home.navigationOptions = {
drawerLabel: 'Home',
drawerIcon: ({ tintColor }) => (
<MaterialIcons
name="move-to-inbox"
size={24}
style={{ color: tintColor }}
/>
),
};
App.navigationOptions = {
drawerLabel: 'App',
drawerIcon: ({ tintColor }) => (
<MaterialIcons name="drafts" size={24} style={{ color: tintColor }} />
),
};
export default DrawerExample = DrawerNavigator(
{
Home: {
path: '/',
screen: Home,
},
App: {
path: '/sent',
screen: App,
},
},
{
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
initialRouteName: 'Home',
contentOptions: {
activeTintColor: '#e91e63',
},
}
);
I don't understand why, each time I put some logic in componentDidMount() DrawerNavigator start to be laggy and does not work properly on Android. I think I'm missing something but I don't know what. If I have to put some logic in a component, where should I put the logic code ? If someone would like to enlighten me:)
use prop detachInactiveScreens={false} in <Drawer.Navigator>
but you also need to
import { enableScreens } from 'react-native-screens'
it solved my lagging issue of drawer.

Categories

Resources