I'm trying to configure deeplinks for my React Native app, following the official documentation, but I haven't been able to make it work. I mean, the app does open if I run adb shell am start -W -a android.intent.action.VIEW -d “crf://" packageName or xcrun simctl openurl booted crf:// but I haven't been able to open a specific screen; it always launches to the app home screen. I'm almost sure it has to do with the nested navigators, since I have a TabbarNavigator inside a DrawerNavigator and a StackNavigator inside all of that. I followed the instructions regarding nested navigators in the documentation and also this post, amongst other things, but no luck. I want to go to the EventHomeScreen, PersonDetailScreen and ProgramSessionDetail
Here is my code:
NavigatorRouter
const mainNavigator = createStackNavigator(
{
[Constants.APP_HOME_SCENE_KEY]:{
screen: AppHomeScreen,
navigationOptions: {
title: 'App Home',
showBack: false,
showSearch: false,
},
path: ''
},
[Constants.EVENT_HOME_SCENE_KEY]:{
screen: navPropMapper()(EventHomeScreen),
navigationOptions:{
title: 'Home',
path: 'event'
}
},
[Constants.ATTENDEE_DETAIL_SCENE_KEY]:{
screen: navPropMapper()(PersonDetailScreen),
navigationOptions:{
title: 'Attendee Detail',
// path: 'person/:user'
path: 'person'
}
},
[Constants.PROGRAM_DETAIL_SCENE_KEY]:{
screen: navPropMapper()(ProgramSessionDetail),
navigationOptions:{
title: 'Program',
path: 'program/:idLecture'
}
}
},
{
initialRouteName: `${Constants.APP_HOME_SCENE_KEY}`,
defaultNavigationOptions: {
header: props => <NavBar {...props} />,
gesturesEnabled: false,
showBack: true,
showHome: false,
showSearch: true,
showWebExplorer: false
}
});
const tabbarNavigator = createBottomTabNavigator({
Main: {
screen: mainNavigator,
path: 'tabs'
},
}, {
tabBarComponent: Tabbar,
});
const drawerNavigator = createDrawerNavigator({
Drawer: {
screen: tabbarNavigator,
navigationOptions:{
drawerLockMode: 'locked-closed',
},
path: 'drawer'
},
}, {
contentComponent: ({ props }) => <DrawerContainer {...props}/>,
drawerPosition: 'right',
unmountInactiveRoutes: true,
defaultNavigationOptions: {
header: null,
}
});
const wraperNavigator = createStackNavigator({
MainComponents: {
screen: drawerNavigator,
path: 'main'
},
[Constants.MODAL_FEEDBACK]:{
screen: navPropMapper()(Modal),
navigationOptions:{
title: 'Feedback',
}
},
[Constants.MODAL_LOGIN]:{
screen: navPropMapper()(ModalLogin),
navigationOptions:{
title: 'Login',
}
},
}, {
mode: 'modal',
cardStyle:{
backgroundColor: 'transparent',
opacity: 1,
},
cardShadowEnabled: true,
cardOverlayEnabled: true,
transparentCard: true,
defaultNavigationOptions: {
header: null,
gesturesEnabled: false,
},
});
export default createAppContainer(wraperNavigator)
App.js
class App extends Component {
render () {
return (
<Provider store={store}>
<RootContainer />
</Provider>
)
}
}
export default App
RootContainer
class CRrootContainer extends Component {
render () {
return (
<View style={styles.applicationView}>
<NavigationRouter uriPrefix={'crf://'}/>
</View>
)
}
}
const mapStateToProps = (state) => {
return {
navState: state.navigation,
}
}
// wraps redux integration
const mapDispatchToProps = (dispatch) => ({
startup: () => dispatch(StartupActions.startup()),
})
export default connect(mapStateToProps, mapDispatchToProps)(rootContainer)
iOS Uri and Scheme
AppDelegate.m
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [RCTLinkingManager application:application openURL:url
sourceApplication:sourceApplication annotation:annotation];
}
URL Types:
Android Scheme:
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="packageName">
<application
android:name=".MainApplication"
android:allowBackup="true"
android:label="#string/app_name"
android:icon="#mipmap/ic_launcher"
android:roundIcon="#mipmap/ic_launcher_round"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="crf"/>
</intent-filter>
</activity>
</application>
</manifest>
The commands I'm trying are:
xcrun simctl openurl booted crf://main/drawer/tabs/event for iOS.
and
adb shell am start -W -a android.intent.action.VIEW -d “crf://main/drawer/tabs/event" packageName for Android (packageName is the name of my app package`
My react-navigation version is 3.6.1 and react-native: 0.59.9.
can you try setting an event listener ?
class CRrootContainer extends Component {
async componentDidMount(){
if (Platform.OS === "android") {
let url = await Linking.getInitialURL();
if(url) this._setDeepLink(url);
} else {
Linking.addEventListener("url", this._handleOpenURL);
}
};
_handleOpenURL = event => {
this._setDeepLink(event.url);
};
_setDeepLink = url => {
const { navigate } = this.props.navigation;
const route = url.replace(/.*?:\/\//g, "");
const routeName = route.split("/")[0];
console.log("root====>", routeName);
if (routeName === "foo") {
navigate("fooRoute"); // to navigate route based on deep link url
}
};
componentWillUnmount() {
Linking.removeEventListener("url");
}
render () {
return (
<View style={styles.applicationView}>
<NavigationRouter uriPrefix={'crf://'}/>
</View>
)
}
}
const mapStateToProps = (state) => {
return {
navState: state.navigation,
}
}
// wraps redux integration
const mapDispatchToProps = (dispatch) => ({
startup: () => dispatch(StartupActions.startup()),
})
export default connect(mapStateToProps, mapDispatchToProps)(rootContainer)
So it ended up beeing a rookie mistake, putting the path: keyword on the wrong place; it needs to go outside the navigationOptions, not inside. Also, path value on drawer navigator and tabbar navigator should be empty.
const mainNavigator = createStackNavigator(
{
[Constants.APP_HOME_SCENE_KEY]:{
screen: AppHomeScreen,
navigationOptions: {
title: 'App Home',
showBack: false,
showSearch: false,
},
path: ''
},
[Constants.EVENT_HOME_SCENE_KEY]:{
screen: navPropMapper()(EventHomeScreen),
navigationOptions:{
title: 'Home',
},
path: 'event'
},
[Constants.ATTENDEE_DETAIL_SCENE_KEY]:{
screen: navPropMapper()(PersonDetailScreen),
navigationOptions:{
title: 'Attendee Detail'
},
path: 'person'
},
[Constants.PROGRAM_DETAIL_SCENE_KEY]:{
screen: navPropMapper()(ProgramSessionDetail),
navigationOptions:{
title: 'Program'
},
path: 'program/:idLecture'
}
},
{
initialRouteName: `${Constants.APP_HOME_SCENE_KEY}`,
defaultNavigationOptions: {
header: props => <NavBar {...props} />,
gesturesEnabled: false,
showBack: true,
showHome: false,
showSearch: true,
showWebExplorer: false
}
});
const tabbarNavigator = createBottomTabNavigator({
Main: {
screen: mainNavigator,
path: ''
},
}, {
tabBarComponent: Tabbar,
});
const drawerNavigator = createDrawerNavigator({
Drawer: {
screen: tabbarNavigator,
navigationOptions:{
drawerLockMode: 'locked-closed',
},
path: ''
},
}, {
contentComponent: ({ props }) => <DrawerContainer {...props}/>,
drawerPosition: 'right',
unmountInactiveRoutes: true,
defaultNavigationOptions: {
header: null,
}
});
const wraperNavigator = createStackNavigator({
MainComponents: {
screen: drawerNavigator,
path: ''
},
[Constants.MODAL_FEEDBACK]:{
screen: navPropMapper()(Modal),
navigationOptions:{
title: 'Feedback',
}
},
[Constants.MODAL_LOGIN]:{
screen: navPropMapper()(ModalLogin),
navigationOptions:{
title: 'Login',
}
},
}, {
mode: 'modal',
cardStyle:{
backgroundColor: 'transparent',
opacity: 1,
},
cardShadowEnabled: true,
cardOverlayEnabled: true,
transparentCard: true,
defaultNavigationOptions: {
header: null,
gesturesEnabled: false,
},
});
export default createAppContainer(wraperNavigator)
Related
I have a tab navigator (with 3 screens) then each of these have also 2 or 3 screen each.
TabNavigator => "A,B,C"
A stack => "1,2,3"
Sometimes I'llgo from 1 to 2, and sometimes from B to 2 (for example), I want that when pressing the back button navigates to the previous screens so 1 for first example and B on the seconds. But right now only goes to 1.
Here is my navigation stack:
const RaidsStack = createStackNavigator({
Raids: { screen: RaidsScreen, },
CreateRaid: { screen: CreateRaidScreen, },
})
const ChatsStack = createStackNavigator({
Chats: { screen: ChatsScreen },
ChatRoom: { screen: ChatRoomScreen, },
})
const SettingsStack = createStackNavigator({
Settings: { screen: SettingsScreen },
EditProfile: { screen: editProfileScreen },
Start: { screen: StartScreen },
})
const StartStack = createStackNavigator({
Tabs: { screen: SettingsScreen },
Register: { screen: editProfileScreen },
Start: { screen: StartScreen },
})
const TabNavigator = createBottomTabNavigator(
{
Raids: { screen: RaidsStack },
Chats: { screen: ChatsStack },
Settings: { screen: SettingsStack }
},
{
tabBarPosition: "bottom",
tabBarComponent: props => {
return (
<Footer>
<FooterTab>
<Button
vertical
active={props.navigation.state.index === 0}
onPress={() => props.navigation.replace('Tabs', {}, NavigationActions.navigate({ routeName: 'RaidsStack' }))}>
<Icon name="egg" />
<Text>Raids</Text>
</Button>
<Button
vertical
active={props.navigation.state.index === 1}
onPress={() => props.navigation.replace('Tabs', {}, NavigationActions.navigate({ routeName: 'Chats' }))}>
<Icon name="mail" />
<Text>Chats</Text>
</Button>
<Button
vertical
active={props.navigation.state.index === 2}
onPress={() => props.navigation.replace('Tabs', {}, NavigationActions.navigate({ routeName: 'Settings' }))}>
<Icon name="settings" />
<Text>Settings</Text>
</Button>
</FooterTab>
</Footer>
);
}
}
)
const AppStack = createStackNavigator({
Start: { screen: StartScreen },
Register: { screen: RegisterScreen },
Tabs: TabNavigator,
Raids: { screen: RaidsScreen },
Chats: { screen: ChatsScreen },
Settings: { screen: SettingsScreen }
}, {
headerMode: 'none',
})
How to do it? I'm new to react and react navigation.
I suggest looking at the documentation https://reactnavigation.org/docs/stack-navigator/
Since you also using nested navigator I suggest looking at this page too https://reactnavigation.org/docs/nesting-navigators/
It’s exactly tells shows you the example of nested navigator with a tab
Hope this material will help you!
navigate works fine however when I replace it with push, it stops working.
<Text
onPress={() => {
//works
this.props.navigation.navigate("VideoPlayer", { id });
// doesn't work
this.props.navigation.push("VideoPlayer", { id });
}}
style={styles.text}
>
{title}
</Text>
How can I get push to work so I can remount components?
I am using a Drawer with a new stack for each route so I can use headers:
const config = {
initialRouteName: "Home",
contentOptions: {
activeTintColor: "#e91e63",
itemStyle: {
flexDirection: "row-reverse"
}
},
drawerWidth: 300,
drawerPosition: "right"
};
const withHeader = (
screen: Function,
routeName: string,
Header
): StackNavigator =>
createStackNavigator(
{
[routeName]: {
screen,
navigationOptions: ({ routeName, props }) => ({
header: props => <Header {...props} />
})
}
},
{
transparentCard: true
}
);
const routes = {
Home: {
screen: withHeader(HomeScreen, "Home", BasicHeader)
},
Links: {
screen: withHeader(LinksScreen, "Links", DrawerHeader)
},
Settings: {
screen: withHeader(SettingsScreen, "Settings", DrawerHeader)
},
VideoEpisodes: {
screen: withHeader(VideoEpisodesScreen, "Video Episodes", DrawerHeader)
},
VideoPlayer: {
screen: withHeader(VideoPlayerScreen, "Video Player", DrawerHeader)
},
TestYourself: {
screen: withHeader(TestYourselfScreen, "Test Yourself", DrawerHeader)
},
MyResults: {
screen: withHeader(MyResultsScreen, "My Results", DrawerHeader)
},
BookmarkedVideos: {
screen: withHeader(
BookmarkedVideosScreen,
"Bookmarked Videos",
DrawerHeader
)
},
Search: {
screen: withHeader(SearchScreen, "Search", DrawerHeader)
},
About: {
screen: withHeader(AboutScreen, "About", DrawerHeader)
}
};
const AppNavigator = createDrawerNavigator(routes, config);
export default createAppContainer(AppNavigator);
.push() is a StackAction method, you have to use a stackNavigator in order to use it
I want to update the react-navigation library V2 to V3 and change part of my code thinking that there would not be any problems but it turns out that I have problems creating createStackNavigator with a screen of type createDrawerNavigator and that in turn contains createBottomTabNavigator.
my code that works with the previous version was:
export const createRootNavigator = (signedIn = false) => {
const commonNavigationOptions = {
headerStyle: {
shadowColor: 'transparent',
elevation: 0
},
headerTintColor: DEFAULT_THEME.topaz
};
const SignedIn = createStackNavigator(
{
Home: {
screen: Drawer('Home'),
navigationOptions: () => ({
headerStyle: {
height: 0
},
header: getSafeArea(DEFAULT_THEME.backgrouncolorHomeSafeArea)
})
},
Cards: {
screen: Tabs('Cards'),
navigationOptions: () => ({
headerStyle: {
height: 0
}
})
},
);
const SignedOut = createStackNavigator(
{
SignIn: {
screen: LoginContainer,
navigationOptions: () => ({
headerStyle: {
height: 0
},
header: getSafeArea(DEFAULT_THEME.dark)
})
},
SelectableCardsList: { screen: SelectableCardsListComponent },
);
return createSwitchNavigator(
{
SignedIn: { screen: SignedIn },
SignedOut: { screen: SignedOut }
},
{
initialRouteName: signedIn ? 'SignedIn' : 'SignedOut'
}
);
};
const Drawer = (initialRoute) => createDrawerNavigator(
{
Home: { screen: Tabs('Home') },
{
initialRouteName: initialRoute,
contentComponent: CustomDrawerComponent
}
);
const Tabs = (initialRouteName) => createBottomTabNavigator(
{
Home: {
screen: HomeContainer,
navigationOptions: {
tabBarLabel: I18n.t('tabs.me')
}
},
Home2: {
screen: Home2,
navigationOptions: {
tabBarLabel: I18n.t('tabs.credentials')
}
},
{
initialRouteName: initialRouteName,
tabBarComponent: ({ navigation }) => <CustomBottomBarComponent navigation={navigation} navigationState={navigation['state']} />,
tabBarOptions: {
style: {
backgroundColor: 'white'
}
}
}
);
try this solution with react-navigation V3 and send me an error
I try the following:
encapsulate createSwitchNavigator in createAppContainer and separate (SignedOut and SignedOut) createStackNavigator out of createSwitchNavigator, the rest is still the same.
export const createRootNavigator = (signedIn = false) => createAppContainer(createSwitchNavigator(
{
SignedIn: { screen: SignedIn },
SignedOut: { screen: SignedOut }
},
{
initialRouteName: signedIn ? 'SignedIn' : 'SignedOut'
}
));
I get the following error: The component for route 'Home' must be a react component. For Example
import MyScreen from './MyScreen';
...
Home : MyScreen,
}
you can also use a navigator:
The problem is located in this part:
const SignedIn = createStackNavigator(
{
Home: {
screen: Drawer,
Also try to change Drawer for any component (a component with a blank screen) and this works, but I can not insert the Tabs in the Drawer.
Thank you very much for your help.
I have a StackNavigation like this:
const AppNavigator = createStackNavigator({
Login: {
screen: Login,
navigationOptions: () => ({
title: 'Login',
headerTintColor: 'white',
headerStyle:{
backgroundColor: '#000',
elevation: 0,
showdowOpacity: 0
},
})
},
Home: {
screen: AppDrawerNavigator,
navigationOptions: () => ({
header: null
})
},
});
With a DrawerNavigator nested inside:
const AppDrawerNavigator = createDrawerNavigator({
Home: {
screen: Home,
navigationOptions: {
drawerLabel: 'Home',
gesturesEnabled: false,
}
},
Favorites: {
screen: Favorites,
navigationOptions: {
drawerLabel: 'Favorites',
}
}
},
{
drawerPosition: 'left',
contentComponent: props => <Drawer {...props} />
});
The initial route of the stack navigator is working fine
Login -> Home
But when I try navigating from Home to Favorites it navigates immediately back to Home after rendering the Favorites screen.
I am using react-navigation#2.11.2 and react-native#0.56.0
With Home being used in both stack and drawer navigator.
There are high chances of name conflicts occurring here.
Try this structure.
const Stack = {
FirstView: {
screen: FirstView
},
SecondView: {
screen: SecondView
},
ThirdView: {
screen: ThirdView
}
};
const DrawerRoutes = {
FirstViewStack: {
name: 'FirstViewStack',
screen: StackNavigator(Stack, { initialRouteName: 'FirstView' })
},
SecondViewStack: {
name: 'SecondViewStack',
screen: StackNavigator(Stack, { initialRouteName: 'SecondView' })
},
ThirdViewStack: {
name: 'ThirdViewStack',
screen: StackNavigator(Stack, { initialRouteName: 'ThirdView' })
},
};
const RootNavigator =
StackNavigator({
Drawer: {
name: 'Drawer',
screen: DrawerNavigator(
DrawerRoutes,
),
},
...Stack
},
{
headerMode: 'none'
}
);
I faced a similar issue when i tried to use a hamburger menu in my Home page (which uses stack navigator to goto other pages).
Check this Git Article also.
Consider the render of the Main component:
render() {
const { isAuthenticated } = this.props;
return (
<View>
{isAuthenticated ? <Dashboard /> : <Login />}
</View>
);
I want to lock the drawer in the Login component. Now i know that i could achieve this if Login wasn't a child of Main this way (in my Router component):
Login: {
screen: Login,
navigationOptions: () => ({
drawerLockMode: 'locked-closed',
}),
},
But since Login is a child of Main and Main has the drawer, Login will automatically have the drawer too. I've tried "overriding" it by calling this in Login:
static navigationOptions = {
drawerLockMode: 'locked-closed',
};
But no success. Here's my Router:
const Stack = {
Main: { screen: Main },
Login: {
screen: Login,
navigationOptions: () => ({
drawerLockMode: 'locked-closed',
}),
},
Outbox: { screen: Outbox },
Dashboard: { screen: Dashboard },
JobList: { screen: JobList },
CreateJob: { screen: CreateJob },
Reporting: { screen: Reporting },
JobDescription: { screen: JobDescription },
};
const DrawerRoutes = {
DrawerStack: {
name: 'DrawerStack',
screen: StackNavigator(
Stack,
{
initialRouteName: C.MAIN,
headerMode: 'none',
navigationOptions: {
gesturesEnabled: false,
},
}),
},
};
export const DrawerNavigation = StackNavigator({
Drawer: {
name: 'Drawer',
screen: DrawerNavigator(DrawerRoutes, {
contentComponent: DrawerPanel,
}),
},
...Stack,
}, { headerMode: 'none' });
Is there a way to achieve this ?