React Native: How to use deep linking with iOS? - react-native

I'm trying to use deep linking in my react native app for ios. I've been following this document by React Navigation.
I added what was needed to my AppDelegate.m and to Xcode:
However I'm confused on how to implement this in my App.js:
const AuthStack = createStackNavigator();
const AuthStackScreen = () => (
<AuthStack.Navigator >
<AuthStack.Screen
name="Ingresar"
component={Login}
options = {{
headerShown: false,
title: "Bienvenido"
}}/>
<AuthStack.Screen name="CrearCuenta"
component={Signup}
options={{title: "Crear cuenta"}}/>
</AuthStack.Navigator>
);
const Tabs = createBottomTabNavigator();
const HomeStack = createStackNavigator();
const DonationsStack = createStackNavigator();
const DonateStack = createStackNavigator();
const InstitutionsStack = createStackNavigator();
const ProfileStack = createStackNavigator();
const DonationsStackScreen = () => (
<DonationsStack.Navigator>
<DonationsStack.Screen name="Pedidos" component={DonationsScreen}/>
<DonationsStack.Screen name="Realizar donación" component={DonationFormScreen}/>
<DonationsStack.Screen name="Donación confirmada" component={DonationConfirmationScreen}
options = {{
headerShown: false
}}/>
</DonationsStack.Navigator>
);
// I omit some of the stack screens here.
const TabsScreen = () => (
<Tabs.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) =>
{
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home-outline';
return <MaterialCommunityIcons name={iconName} size={size} color={color}/>;
} else if (route.name === 'Pedidos') {
iconName = focused ? 'ios-list-box' : 'ios-list';
}
else if (route.name === 'Donar') {
iconName = focused ? 'gift' : 'gift-outline';
return <MaterialCommunityIcons name={iconName} size={size} color={color}/>;
} else if (route.name === 'Perfil') {
iconName = focused ? 'user-alt'
: 'user';
return <FontAwesome5 name={iconName} size={size-4} color={color}/>;
} else if (route.name === 'Instituciones') {
iconName = 'md-business';
}
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
activeTintColor: '#013773',
inactiveTintColor: 'gray',
}}>
<Tabs.Screen name="Home" component={HomeStackScreen}/>
<Tabs.Screen name="Pedidos" component={DonationsStackScreen}/>
<Tabs.Screen name="Donar" component={DonateStackScreen}/>
<Tabs.Screen name="Instituciones" component={InstitutionsStackScreen}/>
<Tabs.Screen name="Perfil" component={ProfileStackScreen}/>
</Tabs.Navigator>
);
const RootStack = createStackNavigator();
const RootStackScreen = ({user}) => (
<RootStack.Navigator headerMode="none">
{user ? (
<RootStack.Screen
name="App"
component={TabsScreen}
options={{animationEnabled: false}}/>
) : (
<RootStack.Screen
name="Auth"
component={AuthStackScreen}
options={{animationEnabled: false}}/>
)}
</RootStack.Navigator>
);
export default function ponte() {
const [isLoading, setIsLoading] = React.useState(false);
const [user, setUser] = React.useState(null);
// Handle user state changes
function onAuthStateChanged(user) {
setUser(user);
if (isLoading) setIsLoading(false);
}
if (isLoading) return null;
useEffect(() => {
SplashScreen.hide();
const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
return subscriber; // unsubscribe on unmount
}, []);
const authContext = React.useMemo(() => {
return {
signIn: (email, password) =>
auth()
.signInWithEmailAndPassword(email.email, password.password)
.then(res => {
setIsLoading(false);
setUser(res.user.uid);
})
,
signUp: () => {
setIsLoading(false);
setUser("test");
},
signOut: () => {
setIsLoading(false);
auth().signOut().then(() => console.log('User signed out!'));
setUser(null);
}
};
}, []);
/*
Here comes my deeplinking.
What I need to do is that when the user clicks on my URL
it goes to Pedidos --> Realizar donación with a parameter called request.
All of this happens inside the DonationsStack
*/
const deepLinking = {
prefixes: ['https://demoApp.com', 'demoApp://'],
config: {
Home: 'HomePath',
RealizarDonacion: {
path: 'Pedidos/Realizar donación/:request',
params: {
request: null,
}
}
} ,
};
return(
<AuthContext.Provider value={authContext}>
<NavigationContainer linking={deepLinking}>
<RootStackScreen user={user} />
</NavigationContainer>
</AuthContext.Provider>
);
}
As you see at the end I provide my deepLinking component to the NavigationContainer.
However when I type demoApp://Pedidos/Realizar donación/44 in Safari I don't get redirected to my app.
I'm not convinced about that space inside the path, however it doesn't work when I create a deepLink to Pedidos only either.
I need my URL to open my app and inside the DonationsStack navigate to Pedidos (DonationsScreen) --> Realizar donación (DonationFormScreen)
Update: My app opens but I don't get redirected to any screen.

Related

Two tabs (Tab.Navigator) using one component to display lists, only data is different, Back button for the details page works wrong

when I tried to refactor the mobile app of 'Notes' from ‘JavaScript Everywhere’ book, a problem
The structure is like this:
RootNavigator(Stack.Navigator)
---AuthLoading(Screen)
---Auth(Stack.Navigator)
--SignIn(Screen)
--SignUp(Screen)
---App(Tab.Navigator)
--FeedStack(Stack.Navigator)
-FeedScreen(Screen)
-NoteScreen(Screen)
--MyNotesStack(Stack.Navigator)
-MyNotesScreen(Screen)
-NoteScreen(Screen)
--FavoritesStack(Stack.Navigator)
-FavoritesScreen(Screen)
-NoteScreen(Screen)
--SettingsStack(Stack.Navigator)
When a user login, the default tab is ‘Feed’ which goes to ‘FeedStack’, and FeedScreen list all the notes created by all the users, click one item of the notes, it goes to a NoteScreen, display the details of that ‘Note’, everything goes well.
When the user choose ‘MyNotes’ tab which goes to ‘MyNoteStack’, it list the notes created by the current user, click one of the ‘note’ item, it goes to NoteScreen, display the details of that ‘note’. However, now, the default focus of the Tab.Navigator is ‘Feed’, and when I click back button in the NoteScreen, it goes back to ‘FeedStack’ which displays all the notes. It is weird!
I can’t understand why I go to the NoteScreen from ‘MyNotes’, but back button leads it to ‘Feed’, how to fix this problem?
And the code is as follows.
In index.js (RootNavigator)
const Stack = createNativeStackNavigator();
const Tab = createBottomTabNavigator();
const AuthStack = createNativeStackNavigator();
const feedStack = createNativeStackNavigator();
const myNotesStack = createNativeStackNavigator();
const favoritesStack = createNativeStackNavigator();
const settingsStack = createNativeStackNavigator();
function FeedStack () {
return (
<feedStack.Navigator
screenOptions={
{headerShown:false}
}
>
<feedStack.Screen name="FeedScreen" component={FeedScreen} />
<feedStack.Screen name="NoteScreen" component={NoteScreen} options={{headerShown:true}}/>
</feedStack.Navigator>
);
}
function MyNotesStack () {
return (
<myNotesStack.Navigator
screenOptions={
{headerShown:false}
}
>
<myNotesStack.Screen name="MyNotesScreen" component={MyNotesScreen} />
<myNotesStack.Screen name="Note" component={NoteScreen} options={{headerShown:true}} />
</myNotesStack.Navigator>
);
}
function FavoritesStack () {
return (
<favoritesStack.Navigator
screenOptions={
{headerShown:false}
}
>
<favoritesStack.Screen name="FavoritesScreen" component={FavoritesScreen} />
<favoritesStack.Screen name="Note" component={NoteScreen} options={{headerShown:true}}/>
</favoritesStack.Navigator>
);
}
function SettingsStack () {
return (
<settingsStack.Navigator
screenOptions={
{headerShown:false}
}
>
<settingsStack.Screen name="SettingsScreen" component={SettingsScreen} />
</settingsStack.Navigator>
);
}
const TabNavigator = () => {
return (
<Tab.Navigator
initialRouteName="MyNotes"
activeColor='#f0f'
inactiveColor='#555'
barStyle={{
backgroundColor:'#999'
}}
screenOptions={({route}) => ({
headerShown: false,
tabBarIcon:({focused, size, color}) => {
let iconName;
if( route.name === 'FeedStack') {
iconName = 'home';
} else if (route.name === 'MyNotesStack') {
iconName = 'bed';
} else if (route.name === 'FavoritesStack') {
iconName = 'star'
} else {
iconName = 'spa'
}
color = focused ? '#f0f' : "#555";
size = focused ? 24 : 20;
return <FontAwesome5 name={iconName} size={size} color={color}/>;
},
})}
>
<Tab.Screen name='FeedStack' component={FeedStack} options={{headerShown: false}} />
<Tab.Screen name='MyNotesStack' component={MyNotesStack} options={{headerShown: false}} />
<Tab.Screen name='FavoritesStack' component={FavoritesStack} options={{headerShown: false}}/>
<Tab.Screen name='SettingsStack' component={SettingsStack} options={{headerShown: false}} />
</Tab.Navigator>
);
};
const Auth= () => {
return (
<AuthStack.Navigator
screenOptions={{headerShown:false}}
>
<AuthStack.Screen name='signIn' component={SignIn}></AuthStack.Screen>
</AuthStack.Navigator>
);
};
const RootNavigator = () => {
return (
<Stack.Navigator initialRouteName='AuthLoading'>
<Stack.Screen name='AuthLoading'
component={AuthLoading}
options={{title:'AuthLoading'}}
>
</Stack.Screen>
<Stack.Screen name='Auth'
component={Auth}
options={{
title: 'Auth',
headerStyle: {
backgroundColor: '#f4511e',
},
headerBackVisible: false,
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
</Stack.Screen>
<Stack.Screen name='App'
component={TabNavigator}
options={{
title: 'App',
headerStyle: { backgroundColor: '#f4511e'},
headerBackVisible:false,
headerTintColor: '#fff',
headerTitleStyle: {fontWeight: 'bold'},
}}
>
</Stack.Screen>
</Stack.Navigator>
);
};
export default RootNavigator;
In FeedScreen.js:
const FeedScreen = () => {
const { data, loading, error } = useQuery(GET_NOTES);
// if the data is loading, our app will display a loading indicator
if(loading)
return <Loading />;
if(error)
return <Text>Error loading notes.</Text>;
// if the query is successful and there are notes, return the feed of notes
return (
<NoteFeed notes={data.notes} />
);
};
In MyNotesScreen.js
const MyNotesScreen = () => {
const { data, loading, error } = useQuery(GET_MY_NOTES);
// if the data is loading, our app will display a loading indicator
if(loading)
return <Loading />;
if(error)
return <Text>Error loading MyNotes.</Text>;
// if the query is successful and there are notes, return the feed of notes
// else if the query is successful and there aren't notes, display a message
if(data.me.notes.length !== 0) {
return <NoteFeed notes={data.me.notes} />;
} else {
return <Text>No notes yet</Text>;
}
// If I don't use <NoteFeed> here, for example, show a button then go to <NoteScreen> it is ok.
// return (
// <View style={styles.container}>
// <Text>My Notes Screen</Text>
// use self-defined button
// <JereButton
// onPress={() => navigation.navigate('Note',{id:'63b94da5ccf7f90023169c3d'})}
// title="Go to a note"
// color={"#882"}
// />
// </View>
// );
};
In NoteFeed.js
const NoteFeed = props => {
// only screen components receive navigation prop automatically!
// if you wish to access the navigation prop in any of your components, you may use the useNavigation hook.
const navigation = useNavigation();
return (
<View style={styles.container}>
<FlatList
data={props.notes}
keyExtractor = {({id}) => id.toString()}
renderItem = {({item}) => (
<Pressable style={styles.feedview}
onPress={() => navigation.navigate('NoteScreen',{id: item.id})}
>
<Text style={styles.text}>{item.content}</Text>
</Pressable>
)}
/>
</View>
);
};
In NoteScreen.js
const NoteScreen = ({navigation, route}) => {
const {id} = route.params;
const { data, loading, error } = useQuery(GET_NOTE, {variables:{id}});
// if the data is loading, our app will display a loading indicator
if(loading)
return <Loading />;
if(error)
return <Text>Error Note not found.</Text>;
return (
<Note note={data.note} />
);
};
Thank you for your help.
I tried to replace useNavigation() to props solution, the issue is the same. Then I tried to do not use in 'MyNotes' to show the ‘note’, it is OK, but it doesn’t comply with the design.

Troubleshooting Navigation Issues on the Onboarding to HomeScreen Transition

Hello I couldn't navigate from Onboarding to HomeScreen. I'm not sure what I do wrong. It gives error such as The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization
for more details.
//AppRoot.js
const [isFirstLaunch, setFirstLaunch] = useState(null);
useEffect(() => {
AsyncStorage.getItem("onboardingCompleted").then((value) => {
if (value == null) {
setFirstLaunch(true);
} else {
setFirstLaunch(false);
}
});
}, []);
return (
<NavigationContainer
ref={navigationRef}
onReady={() =>
(routeNameRef.current =
navigationRef?.current?.getCurrentRoute()?.name ?? "")
}
onStateChange={() => {
const previousRouteName = routeNameRef.current;
const currentRouteName =
navigationRef?.current?.getCurrentRoute()?.name;
trackScreenView(previousRouteName, currentRouteName);
console.log(previousRouteName, " -> ", currentRouteName);
// Save the current route name for later comparison
routeNameRef.current = currentRouteName;
}}
theme={themeToSet === "dark" ? DarkModeTheme : LightModeTheme}
>
<Loader visible={isLoading} />
{isFirstLaunch == false ? <HomeScreen /> : <OnboardingScreen />}
</NavigationContainer>
);
// Onboarding
const OnboardingScreen = () => {
const navigation = useNavigation();
const [currentPage, setCurrentPage] = useState(0);
const pages = [
<WelcomeScreen onGetStarted={() => setCurrentPage(1)} />,
<PersonalizationScreen1 />,
<PersonalizationScreen2 />,
<NotificationScreen />,
<WidgetScreen />,
<PaywallScreen />,
<EndScreen />,
];
const onContinue = () => {
if (currentPage === pages.length - 1) {
AsyncStorage.setItem("onboardingCompleted", "true");
} else {
setCurrentPage(currentPage + 1);
}
};
return <View>{React.cloneElement(pages[currentPage], { onContinue })}</View>;
};
export default OnboardingScreen;
//HomeScreen
const Tab = createBottomTabNavigator();
const HomeBottomNavigation = () => {
const { colors } = useTheme();
const { t } = useTranslation();
return (
<Tab.Navigator
id="BottomNavigation"
initialRouteName="Users"
screenOptions={({ route, navigation }) => ({
headerShown: false,
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === "UsersStack") {
iconName = focused ? "people" : "people-outline";
} else if (route.name === "SettingsStack") {
iconName = focused ? "settings" : "settings-outline";
} else if (route.name === "Feedback") {
iconName = focused ? "newspaper" : "newspaper-outline";
} else if (route.name === "More") {
iconName = focused ? "apps" : "apps-outline";
}
return <Icon name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: colors.text,
tabBarInactiveTintColor: "gray",
unmountOnBlur: true,
})}
>
<Tab.Screen
name="UsersStack"
component={UsersStackScreen}
options={{ headerShown: false, title: t("users") }}
/>
<Tab.Screen
name="SettingsStack"
component={SettingsStackScreen}
options={{ headerShown: false, title: t("settings") }}
/>
<Tab.Screen
name="Feedback"
component={FeedbackScreen}
options={{ headerShown: false, title: t("feedback") }}
/>
<Tab.Screen
name="More"
component={MoreScreen}
options={{ headerShown: false, title: t("more") }}
/>
</Tab.Navigator>
);
};
export default HomeBottomNavigation;
//Onboarding Stack
const Stack = createNativeStackNavigator();
const OnboardingStackNavigation = () => {
return (
<Stack.Navigator
id="OnboardingNavigation"
initialRouteName="Onboarding"
screenOptions={({ route, navigation }) => {
({ headerShown: false });
}}
>
<Stack.Screen
name="Onboarding"
component={OnboardingScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Home"
component={UsersScreen}
options={{ headerShown: false }}
/>
</Stack.Navigator>
);
};
I have tried passing setFirstLaunch to onboarding screen and tried to manage that.
I have tried navigating inside Onboarding.
I have tried button triggered navigation.

React Native mixed Navigation

I am about to crate an app that has a main Drawer Navigator (sidemenu). On some specific pages I want to add an additional Tabsmenu to the page. How can I achieve that?
This is the Drawer Navigator:
const Drawer = createDrawerNavigator()
function myLogout(){
navigation.navigate("LoginScreen");
}
const DrawerNavigator = () => {
// Load the icon font before using it
const [fontsLoaded] = useFonts({ IcoMoon: require('../../assets/icomoon/zepra_icons.ttf') });
if (!fontsLoaded) {
return <AppLoading />;
}
return (
<Drawer.Navigator initialRouteName="Home" drawerContent={props => {
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<DrawerItem label="Logout" labelStyle={{marginLeft:-18, fontSize: 15,}}
icon={({ focused, color, size }) => <Icon2 style={{marginLeft:8}} color={focused ? '#1585b5' : '#6cbabf'} size={26} name={focused ? 'log-out-outline' : 'log-out-outline'} /> }
onPress={() => props.navigation.navigate("StartScreen") } />
</DrawerContentScrollView>
)
}}>
...
<Drawer.Screen name="Impressum" component={ImpressumStackNavigator}
options={{
drawerLabel: 'Impressum',
drawerLabelStyle:{marginLeft:-18, fontSize: 15,},
drawerIcon: ({focused, size}) => (
<Icon2 style={{marginLeft:7}} name='ios-information-circle-outline' size={26} color={focused ? '#1585b5' : '#6cbabf'} />
)}}/>
</Drawer.Navigator>
)
}
export default DrawerNavigator
this is the Dashboard Page:
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native'
import DrawerNavigator from './DrawerNavigator'
function App() {
return (
<DrawerNavigator />
);
}
export default App;
and this is the page with the tabs:
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import DrawerNavigator from './DrawerNavigator';
//#################### BUTTON-TAB erstellen #####################
const Tab = createBottomTabNavigator();
function MainContainer({ route, navigation }) {
//function MainContainer({ }) {
// Load the icon font before using it
const [fontsLoaded] = useFonts({ IcoMoon: require('../../assets/icomoon/zepra_icons.ttf') });
const { data } = route.params;
if (!fontsLoaded) {
return <AppLoading />;
}
return (
<Tab.Navigator
initialRouteName={projectsName}
screenOptions={({ route }) => ({
headerShown: false,
tabBarIcon: ({ focused, color, size }) => {
let iconName;
let rn = route.name;
if (rn === projectsName) {
iconName = focused ? 'PROJEKTE_ALLE' : 'PROJEKTE_ALLE';
} else if (rn === waermeschutzName) {
iconName = focused ? 'HAUS_3' : 'HAUS_3';
} else if (rn === begehungenName) {
iconName = focused ? 'NOTIZ_ERSTELLEN' : 'NOTIZ_ERSTELLEN';
}
// You can return any component that you like here!
return <Icon name={iconName} size={43} color={color} />;
},
'tabBarActiveTintColor': '#4283b1',
'tabBarInactiveTintColor': '#5db8bd',
'tabBarStyle':{ 'paddingTop':4, 'height':90 },
'tabBarLabelStyle':{ 'paddingTop':3, 'fontSize':13 }
})}>
<Tab.Screen name={projectsName} component={ProjectsScreen} initialParams={{ data }} />
<Tab.Screen name={waermeschutzName} component={WaermeschutzScreen} />
<Tab.Screen name={begehungenName} component={BegehungenScreen} />
</Tab.Navigator>
);
}
export default MainContainer;
How can I add the DrawerNavigator to the Tabspage now?
Thank you for your help

React Native pass params through tabs.screen

how can I pass params or an object to a Tabs-Screen when clicking on that tab in React Nativ? This is the code of a bottom Tab Navigation on one page of the app.
const Tab = createBottomTabNavigator();
function MainContainer({ route, navigation }) {
//function MainContainer({ }) {
// Load the icon font before using it
const [fontsLoaded] = useFonts({ IcoMoon: require('../../assets/icomoon/zepra_icons.ttf') });
const { data } = route.params;
console.log('data:');
console.log(data);
if (!fontsLoaded) {
return <AppLoading />;
}
return (
<Tab.Navigator
initialRouteName={projectsName}
screenOptions={({ route }) => ({
headerShown: false,
tabBarIcon: ({ focused, color, size }) => {
let iconName;
let rn = route.name;
if (rn === projectsName) {
iconName = focused ? 'PROJEKTE_ALLE' : 'PROJEKTE_ALLE';
} else if (rn === waermeschutzName) {
iconName = focused ? 'HAUS_3' : 'HAUS_3';
} else if (rn === begehungenName) {
iconName = focused ? 'NOTIZ_ERSTELLEN' : 'NOTIZ_ERSTELLEN';
}
return <Icon name={iconName} size={43} color={color} />;
},
'tabBarActiveTintColor': '#4283b1',
'tabBarInactiveTintColor': '#5db8bd',
'tabBarStyle':{ 'paddingTop':4, 'height':90 },
'tabBarLabelStyle':{ 'paddingTop':3, 'fontSize':13 }
})}>
<Tab.Screen name={projectsName} component={ProjectsScreen} />
<Tab.Screen name={waermeschutzName} component={WaermeschutzScreen} />
<Tab.Screen name={begehungenName} component={BegehungenScreen} />
</Tab.Navigator>
);
}
export default MainContainer;
I have tryed several ways but could not get it to work. Does anyone have a working example or cane someone help me with my code??
Thank you
You could use the initialParams prop for the Tab.Screen components in order to pass the initial params to each tab.
Here is an example on how to do this for your first tab screen.
<Tab.Screen name={projectsName} component={ProjectsScreen} initialParams={{ data }} />

Making react native TopNavigation bar absolute

So what i want to achieve is a navigation bar that has a background color of blue and have a 60% opacity so that it can show the image behind it. I can only achieve this by making the header's position an absolute but it wont let me click the back button.
here is my code stacknavigator
const UnregisteredNavigator = () => {
return (
<Stack.Navigator
screenOptions={({ navigation,route }) => ({
headerStyle: {
height: 60
},
header: ({}) => {
return (
<ThemedTopNavigation navigation={navigation} route={route} />
);
},
})}
>
<Stack.Screen name="LandingScreen" component={LandingScreen} options={{headerShown:false}} />
<Stack.Screen name="Login" component={SignInScreen} />
</Stack.Navigator>
);
};
and here is my ThemedTopNavigation code
const TopNavigationBar = ({ navigation: { goBack },route,eva }) => {
const themeContext = React.useContext(ThemeContext);
console.log("styled", eva);
const [menuVisible, setMenuVisible] = React.useState(false);
const [checked, setChecked] = React.useState(false);
const onCheckedChange = (isChecked) => {
themeContext.toggleTheme()
setChecked(isChecked);
};
const backFunction = () => {
alert("back");
// goBack()
};
const renderBackAction = () => (
<TopNavigationAction style={{backgroundColor:"red",zIndex: 1, position:'relative'}} icon={BackIcon} onPress={backFunction}/>
);
return (
<Layout style={eva.style.navLayout}>
<TopNavigation
style={[eva.style.navContainer, styles]}
alignment='center'
title={()=>(<Button>CLICK</Button>)}
accessoryLeft={renderBackAction}
/>
</Layout>
);
};
I am using UI kitten. anyone can help me?