I am learning react-native by reading some blogs and trying to replicate the concepts on a test application that uses Drawer (Home and About screens), Stack (Home -> Settings) and Tab (Home and About Screens) navigations. So far, so good.
I am using 'react-native-localize', 'i18n-js' and 'lodash.memoize' in order to make the app multi language. During its startup, the application successfully determines the current locale from the device settings and display the correct contents based on the locale.
However, I want to be able to change it dynamically by pressing the toggle language button on Settings screen. I don't know how to change the locale and make the whole application (drawer menu option names, bottom tab names and all screens text contents) re-render and display text from the new locale. Could someone help me, please?
App.js
import * as i18n from './src/utils/i18n';
const App = () => {
const [locale, setLocale] = useState(i18n.DEFAULT_LANGUAGE);
const localizationContext = useMemo(
() => ({
t: (scope, options) => i18n.t(scope, {locale, ...options}),
locale,
setLocale
}),
[locale],
);
const handleLocalizationChange = useCallback(
(newLocale) => {
const newSetLocale = i18n.setI18nConfig(newLocale);
setLocale(newSetLocale);
},
[locale],
);
useEffect(() => {
handleLocalizationChange();
RNLocalize.addEventListener('change', handleLocalizationChange);
return () => {
RNLocalize.removeEventListener('change', handleLocalizationChange);
};
}, []);
return (
<LocalizationContext.Provider value={localizationContext}>
<NavigationContainer>
<DrawerNavigator localizationChange={handleLocalizationChange}/>
</NavigationContainer>
</LocalizationContext.Provider>
);
};
export default App;
i18n.js
import {I18nManager} from 'react-native';
import * as RNLocalize from 'react-native-localize';
import i18n from 'i18n-js';
import memoize from 'lodash.memoize';
export const DEFAULT_LANGUAGE = 'en';
export const translationGetters = {
'en': () => require('../locales/en.json'),
'es': () => require('../locales/es.json'),
};
export const translate = memoize(
(key, config) => i18n.t(key, config),
(key, config) => (config ? key + JSON.stringify(config) : key),
);
export const t = translate;
export const setI18nConfig = (codeLang = null) => {
// Fallback if no available language fits
const fallback = {languageTag: DEFAULT_LANGUAGE, isRTL: false};
const lang = codeLang ? {languageTag: codeLang, isRTL: false} : null;
const {languageTag, isRTL} = lang
? lang
: RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) ||
fallback;
// Enables fallbacks
i18n.fallbacks = true;
// Clear translation cache
translate.cache.clear();
// Update layout direction
I18nManager.forceRTL(isRTL);
// Set i18n-js config
i18n.translations = {[languageTag]: translationGetters[languageTag]()};
i18n.locale = languageTag;
return languageTag;
};
DrawerNavigation.js
import React, { useContext } from "react";
import { AboutStackNavigator } from "./StackNavigator";
import TabNavigator from "./TabNavigator";
import { createDrawerNavigator } from "#react-navigation/drawer";
import LocalizationContext from '../providers/LocalizationContext';
const Drawer = createDrawerNavigator();
const DrawerNavigator = () => {
const {t} = useContext(LocalizationContext);
return (
<Drawer.Navigator
drawerContentOptions={{
activeTintColor: 'gray',
inactiveTintColor: 'gray',
}}
>
<Drawer.Screen
name="Home"
component={ TabNavigator }
options={{
title: t('title_home')
}}
/>
<Drawer.Screen
name="About"
component={ AboutStackNavigator }
options={{
title: t('title_about')
}}
/>
</Drawer.Navigator>
);
};
export default DrawerNavigator;
StackNavigator.js
import React, { useContext } from "react";
import { createStackNavigator } from "#react-navigation/stack";
import Home from "../screens/Home";
import About from "../screens/About";
import Settings from "../screens/Settings";
import LocalizationContext from '../providers/LocalizationContext';
const Stack = createStackNavigator();
const MainStackNavigator = () => {
const {t} = useContext(LocalizationContext);
return (
<Stack.Navigator screenOptions={screenOptionStyle}>
<Stack.Screen name="Home" component={ Home } options={{ headerShown:false }} />
<Stack.Screen name="About" component={ About } options={{ title: t('title_about') }}/>
<Stack.Screen name="Settings" component={ Settings } options={{ title: t('title_settings') }}/>
</Stack.Navigator>
);
};
const AboutStackNavigator = () => {
const {t} = useContext(LocalizationContext);
return (
<Stack.Navigator screenOptions={screenOptionStyle}>
<Stack.Screen name="About" component={ About } options={{ title: t('title_about') }} />
</Stack.Navigator>
);
};
const SettingsStackNavigator = () => {
const {t} = useContext(LocalizationContext);
return (
<Stack.Navigator screenOptions={screenOptionStyle}>
<Stack.Screen name="Settings" component={ Settings } options={{ title: t('title_settings') }} />
</Stack.Navigator>
);
};
const screenOptionStyle = {
headerStyle: {
backgroundColor: 'white',
},
headerTintColor: "gray",
headerBackTitle: "Back",
};
export {
MainStackNavigator,
AboutStackNavigator,
SettingsStackNavigator
};
TabNavigator.js
import React, { useContext } from 'react';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { MainStackNavigator, AboutStackNavigator } from './StackNavigator';
import LocalizationContext from '../providers/LocalizationContext';
const Tab = createBottomTabNavigator();
const BottomTabNavigator = () => {
const {t} = useContext(LocalizationContext);
return (
<Tab.Navigator
tabBarOptions={{
activeTintColor: 'black',
inactiveTintColor: 'gray',
activeBackgroundColor: 'white',
inactiveBackgroundColor: 'white',
labelStyle: {
fontSize: 12,
},
}} >
<Tab.Screen name="Home" component={ MainStackNavigator } options={{ title: t('title_home') }} />
<Tab.Screen name="About" component={ AboutStackNavigator } options={{ title: t('title_about'), headerShown:false }} />
</Tab.Navigator>
);
}
export default BottomTabNavigator;
Home.js
import React, { useContext } from "react";
import { View, SafeAreaView, StyleSheet, Text, StatusBar } from "react-native";
import { Header } from 'react-native-elements';
import LocalizationContext from '../providers/LocalizationContext';
const Home = ({ navigation }) => {
const {t} = useContext(LocalizationContext);
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor='white' />
<Header
statusBarProps={{ barStyle: 'dark-content', backgroundColor: 'gray' }}
placement="center"
leftComponent={{ icon: 'menu', color: 'white', onPress: () => navigation.openDrawer() }}
centerComponent={{ text: 'My App', style: styles.centerComponent }}
rightComponent={{ icon: 'settings', color: 'white', onPress: () => navigation.navigate("Settings") }}
containerStyle={{ backgroundColor: 'gray', justifyContent: 'space-around' }}
/>
<View style={styles.center}>
<Text>{t('text_home')}</Text>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
center: {
flex: 1,
backgroundColor: 'white',
justifyContent: "center",
alignItems: "center",
textAlign: "center",
},
container: {
flex: 1,
backgroundColor: 'white',
},
centerComponent: {
color: 'white',
fontWeight: 'bold',
fontSize: 20
},
});
export default Home;
Settings.js
import React, { useContext, useState } from "react";
import { View, StyleSheet, Text, StatusBar, Button } from "react-native";
import LocalizationContext from '../providers/LocalizationContext';
const Settings = () => {
const {t} = useContext(LocalizationContext);
const [locale, setLocale] = useState('en');
const toggleLanguage = (locale) => {
setLocale(locale);
console.log(locale);
};
return (
<View style={styles.center}>
<StatusBar barStyle="dark-content" backgroundColor='white' />
<View style={styles.center}>
<Text>{t('text_setings')}</Text>
<Button onPress={() => toggleLanguage(locale == "es" ? "en" : "es")} title={locale == "es" ? "EN" : "ES"} />
</View>
</View>
);
};
const styles = StyleSheet.create({
center: {
flex: 1,
backgroundColor: 'white',
justifyContent: "center",
alignItems: "center",
textAlign: "center",
},
});
export default Settings;
To allow changes in language display, you may use the following method (assuming that you want to display English , French and German) :
make a globals.js with the following (add more items if you need):
module.exports = {
eunit:["Eldery Unit", "Unité des personnes âgées", "Senioreneinheit"],
skeysearch:["Search by Name", "Recherche par nom", "Suche mit Name"],
sstatus: ["Status", "Statut", "Status"]
};
include the following line at the top of your js pages:
GLOBAL = require('./globals');
In the places where you want to display different language, use the following:
GLOBAL.skeysearch[this.state.lang]
change the lang state according to your needs. (say by clicking a button),
So ,
this.state.lang=0 for English
this.state.lang=1 for French
this.state.lang=2 for German
Related
I am make using react-native. I recently had to update my react-navigation package to version 6. The issue is my drawer will no longer open and I cannot figure out how to fix it.
This is may code for my navigation:
import React from 'react';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { createStackNavigator } from '#react-navigation/stack';
import { StyleSheet, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/dist/FontAwesome5';
import IonIcon from 'react-native-vector-icons/dist/Ionicons';
import { useNavigation } from '#react-navigation/native';
import { HomeScreen } from '../../screens/app/home/home.screen';
import HistoryScreen from '../../screens/app/history/history.screen';
import { SignoffScreen } from '../../screens/app/signoff/signoff.screen';
import NotificationsScreen from '../../screens/app/notifications/notifications.screen';
import useTheme from '../../theme/hooks/useTheme';
import { AppStackList, AppStackProps, DrawerList } from './types';
import { Colors } from '../../theme/Variables';
import CustomDrawerContent from '../../components/molecules/custom-drawer';
import { common } from '../../theme/Common';
import { FormScreen } from '../../screens/app/form/form.screen';
import { Menu } from '../../assets';
const AppStack = createStackNavigator<AppStackList>();
const Drawer = createDrawerNavigator<DrawerList>();
const renderIcon = (name: string, ion: boolean) => {
if (ion) {
return <IonIcon name={name} style={styles.iconStyle} />;
}
return <Icon name={name} style={styles.iconStyle} />;
};
const NotificationsNavigator = () => {
const { Gutters } = useTheme();
const navigation = useNavigation<AppStackProps>();
return (
<TouchableOpacity
style={(common.navIconStyle, Gutters.regularRMargin)}
delayPressIn={0}
onPress={navigation.navigate('Notifications', { screen: 'NotificationsScreen' })}
>
<IonIcon name="notifications-outline" style={common.navIconStyle} />
</TouchableOpacity>
);
};
const MenuNavigator = () => {
const navigation = useNavigation<AppStackProps>();
return (
<TouchableOpacity>
<Menu name="notifications-outline" style={common.navIconStyle} />
</TouchableOpacity>
);
};
const historyDrawerOptions = {
headerShown: false,
title: '',
drawerIcon: () => renderIcon('tasks', false),
headerTintColor: Colors.black,
headerRight: NotificationsNavigator,
};
const AppNavigator = () => {
const { Custom } = useTheme();
return (
<AppStack.Navigator screenOptions={Custom.globalNavigatorScreenOptions}>
<AppStack.Screen
name="App Home"
component={DrawerNavigator}
options={{ headerShown: false }}
/>
<AppStack.Screen
name="NotificationsScreen"
component={NotificationsScreen}
options={{ headerShown: false }}
/>
<AppStack.Screen name="FormScreen" component={FormScreen} options={{ headerShown: false }} />
<AppStack.Screen
name="SignoffScreen"
component={SignoffScreen}
options={{ headerShown: false }}
/>
</AppStack.Navigator>
);
};
const DrawerNavigator = () => (
<Drawer.Navigator
drawerStyle={styles.drawerStyle}
drawerContentOptions={{
activeTintColor: Colors.primary,
inactiveTintColor: Colors.white,
labelStyle: {
color: Colors.white,
},
}}
drawerContent={() => <CustomDrawerContent />}
>
<Drawer.Screen
name="Home"
component={HomeScreen}
options={{
headerShown: true,
headerTitle: '',
headerTransparent: true,
headerStyle: {
height: 120,
backgroundColor: '#fff',
},
headerTitleAlign: 'center',
headerTintColor: '#002C5F',
headerRight: NotificationsNavigator,
headerRightContainerStyle: {
width: 100,
marginRight: 8,
},
headerLeft: MenuNavigator,
drawerActiveTintColor: Colors.white,
drawerInactiveTintColor: Colors.white,
drawerLabelStyle: { fontSize: 15 },
}}
/>
<Drawer.Screen name="History" component={HistoryScreen} options={historyDrawerOptions} />
</Drawer.Navigator>
);
export default AppNavigator;
The draw was working before the update but now after it wont open? My NotificationsNavigator will also not open to its screen. Can anyone help???
How to add submenu or tabs in the upper part in connection with the lower button and show the name of the lower button in the header of the application.
import React from 'react';
import { createStackNavigator } from 'react-navigation-stack';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { createAppContainer } from 'react-navigation';
import { MaterialIcons, MaterialCommunityIcons } from '#expo/vector-icons';
import Splash from '../screens/Splash';
import NewsListScreen from '../screens/NewsListScreen';
import NewsItemScreen from '../screens/NewsItemScreen';
const StackNavigator = createStackNavigator({
//Splash: {screen: Splash},
News: {
screen: NewsListScreen
},
NewsItem: {
screen: NewsItemScreen,
navigationOptions: {
headerTitle: 'News Item'
}
}
});
const BottomTabNavigator = createBottomTabNavigator({
Home: {
screen: StackNavigator,
navigationOptions: {
tabBarIcon: () => <MaterialIcons name="home" size={24} />
}
},
News: {
screen: StackNavigator,
navigationOptions: {
tabBarIcon: () => <MaterialCommunityIcons name="newspaper-variant-outline" size={24} />
}
}
})
export default createAppContainer(BottomTabNavigator);
What I want to achieve is the following:
As you can see, the bottom button [News] has three referential buttons in the upper part [FEATURED], [RELEVANT], [SEARCH] and, in addition to that, it recovers the name of the bottom button and adds it to the application header below the top buttons.
[EDITED]
Your NewsListScreen screen, instead of just being a tab bar component, can be something like that:
function NewsListScreen = (props) => (
<View>
<Text>News</Text>
<TabBarComponent {...props} />
</View>
)
function TabBarComponent = createMaterialTopTabNavigator({
featured: ... ,
relevant: ... ,
search: ... ,
})
That being said, if you can, you should update your project to react navigation v5, which is I think much more user friendly.
Final Output:
Implementation using react-navigation v5 :
import * as React from 'react';
import { Text, View, StyleSheet, Dimensions } from 'react-native';
import Constants from 'expo-constants';
import {
NavigationContainer,
useNavigationState,
} from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { createMaterialTopTabNavigator } from '#react-navigation/material-top-tabs';
const TopTab = createMaterialTopTabNavigator();
const BottomTab = createBottomTabNavigator();
/*
Here I am using MaterialTopTabNavigator instead of StackNavigator,
which is a better choice based on UI snapshot in this case.
*/
function NewsTopTabs() {
/*
in case you want to set the title dynamically then you can use the
useNavigationState hook but as there are just two bottom tabs so
I am using hardcoded values.
*/
const nav = useNavigationState((state) => state);
return (
<>
<View style={styles.titleBar}>
<Text style={styles.title}>News</Text>
</View>
<TopTab.Navigator>
<TopTab.Screen name="Featured" component={Featured} />
<TopTab.Screen name="Relevant" component={Relevant} />
<TopTab.Screen name="Search" component={Search} />
</TopTab.Navigator>
</>
);
}
function MatchesTopTabs() {
return (
<>
<View style={styles.titleBar}>
<Text style={styles.title}>Matches</Text>
</View>
<TopTab.Navigator>
<TopTab.Screen name="Featured" component={Featured} />
<TopTab.Screen name="Relevant" component={Relevant} />
<TopTab.Screen name="Search" component={Search} />
</TopTab.Navigator>
</>
);
}
function MyBottomTabs({ navigation }) {
return (
<BottomTab.Navigator>
<BottomTab.Screen name="News" component={NewsTopTabs} />
<BottomTab.Screen name="Matches" component={MatchesTopTabs} />
</BottomTab.Navigator>
);
}
//[FEATURED], [RELEVANT], [SEARCH]
export default function App() {
return (
<NavigationContainer>
<MyBottomTabs />
</NavigationContainer>
);
}
//[FEATURED], [RELEVANT], [SEARCH]
const Featured = () => {
const nav = useNavigationState((state) => state);
return (
<View style={styles.screen}>
<Text>Featured</Text>
</View>
);
};
const Relevant = () => {
const nav = useNavigationState((state) => state);
return (
<View style={styles.screen}>
<Text>Relevant</Text>
</View>
);
};
const Search = () => {
const nav = useNavigationState((state) => state);
return (
<View style={styles.screen}>
<Text>Search</Text>
</View>
);
};
const styles = StyleSheet.create({
titleBar: {
height: 50,
width: Math.round(Dimensions.get('window').width),
backgroundColor: 'white',
justifyContent: 'center',
borderBottomColor: "lightgrey",
borderBottomWidth: 1,
paddingLeft: 10,
},
title: {
fontSize: 20,
fontWeight: 'bold',
},
screen:{
flex: 1,
justifyContent: "center",
alignItems: "center"
}
});
Working Example: Expo Snack
I need to render a modal when the user press in the middle button. I'm using react-native-raw-bottom-sheet library to provide a Modal to my application.
I tried to pass a prop isFocused={props.navigation.isFocused} inside the <Tab.Screen> but the problem is when I pass the props to based if is or not focused in the the is rendered two times instead one.
I also tried to trigger the modal direct by the but without success.
My problematic is, when the user press the i need render the new that will contain the hole logic of the content in the modal and also will render the modal.
My tab.routes.js
import React from 'react';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import TabButton from '../components/Tab/Button';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Home from '../containers/Home';
import Adjustment from '../containers/Adjustment';
import Graphic from '../containers/Graphic';
import Help from '../containers/Help';
import NewTransaction from '../containers/NewTransaction';
const icons = {
Home: {
name: 'home',
},
Graphic: {
name: 'pie-chart',
},
NewTransaction: {
name: 'notifications-none',
},
Help: {
name: 'help-outline',
},
Adjustment: {
name: 'settings',
},
};
const Tab = createBottomTabNavigator();
const TabRoutes = () => (
<Tab.Navigator
initialRouteName="HomeScreen"
screenOptions={({route, navigation}) => ({
tabBarIcon: ({color, size, focused}) => {
if (route.name === 'NewTransaction') {
return <TabButton focused={focused} onPress={() => navigation.navigate('NewTransaction')} />;
}
const {name} = icons[route.name];
return <Icon name={name} size={size} color={color} />;
},
})}
tabBarOptions={{
keyboardHidesTabBar: true,
activeTintColor: '#f8b006',
inactiveTintColor: '#1C3041',
style: {
height: 60,
},
iconStyle: {
marginTop: 5,
},
labelStyle: {
fontSize: 12,
marginBottom: 10,
},
}}>
<Tab.Screen
options={{
title: 'Home',
}}
name="Home"
component={Home}
/>
<Tab.Screen
options={{
title: 'Gráfico',
}}
name="Graphic"
component={Graphic}
/>
<Tab.Screen
options={{
title: '',
}}
name="NewTransaction"
component={NewTransaction}
/>
<Tab.Screen
options={{
title: 'Ajuda',
}}
name="Help"
component={Help}
/>
<Tab.Screen
options={{
title: 'Ajustes',
}}
name="Adjustment">
{(props) => (
<Adjustment isVisible={props.navigation.isFocused()} onPress={() => props.navigation.navigate('Home')} />
)}
</Tab.Screen>
</Tab.Navigator>
);
export default TabRoutes;
Tab/Button/index.js
import React from 'react';
import {TouchableWithoutFeedback} from 'react-native-gesture-handler';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Button from './styles';
const TabButton = ({onPress, focused}) => {
return (
<TouchableWithoutFeedback onPress={onPress}>
<Button focused={focused}>
<Icon name="add" size={35} color={'white'} />
</Button>
</TouchableWithoutFeedback>
);
};
export default TabButton;
And the component that will display the modal content
import React from 'react';
import {Text, View} from 'react-native';
const NewTransaction = ({isVisible}) => {
return (
<View>
<Text>Welcome to NewTransactions </Text>
</View>
);
};
export default NewTransaction;
This is my home tab it looks like to you
I custom tab bar with code but the add button is not a screen it is just a button and popup options to select
import {hideModalCreate, showModalCreate} from '#features/loading/actions';
import CreateYCTV from '#features/main/CreateYCTV';
import HomeScreen from '#features/main/Home';
import Manager from '#features/main/Manager';
import Notification from '#features/main/Notification';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import images from '#res/icons';
import * as React from 'react';
import {Image, Pressable, View} from 'react-native';
import {Text, useTheme} from 'react-native-paper';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {connect} from 'react-redux';
import ProductStack from './ProductStack';
const Tab = createBottomTabNavigator();
function MyTabBar({
state,
descriptors,
navigation,
showModalCreate,
hideModalCreate,
isShowModalCreate,
}) {
const {colors} = useTheme();
const insets = useSafeAreaInsets();
const focusedOptions = descriptors[state.routes[state.index].key].options;
if (focusedOptions.tabBarVisible === false) {
return null;
}
return (
<View
style={{
flexDirection: 'row',
backgroundColor: '#FFFFFF',
paddingBottom: Math.max(insets.bottom, 0),
}}>
{state.routes.map((route, index) => {
const {options} = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const getSourceImage = (isFocused) => {
switch (route.name) {
case 'home':
return isFocused ? images.tab_home1 : images.tab_home;
case 'loans':
return isFocused ? images.tab_searching1 : images.tab_searching;
case 'notification':
return isFocused ? images.notifications1 : images.notifications;
case 'manager':
return isFocused
? images.tab_paper_folder1
: images.tab_paper_folder;
default:
return images.tab_add;
}
};
const onPress = () => {
if (route.name === 'create') {
if (isShowModalCreate) {
hideModalCreate();
return;
}
showModalCreate();
return;
}
hideModalCreate();
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<Pressable
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 8,
backgroundColor: 'white',
}}>
<Image source={getSourceImage(isFocused)} />
{route.name != 'create' ? (
<Text
style={{
color: isFocused ? colors.primary : colors.placeholder,
fontSize: 10,
marginTop: 4,
}}>
{label}
</Text>
) : null}
</Pressable>
);
})}
</View>
);
}
const Tabbar = ({showModalCreate, hideModalCreate, isShowModalCreate}) => {
return (
<Tab.Navigator
tabBar={(props) => (
<MyTabBar
isShowModalCreate={isShowModalCreate}
showModalCreate={showModalCreate}
{...props}
hideModalCreate={hideModalCreate}
/>
)}>
<Tab.Screen
name="home"
component={HomeScreen}
options={{
title: 'Trang chủ',
}}
/>
<Tab.Screen
name="loans"
component={ProductStack}
options={{
title: 'Sản phẩm',
}}
/>
<Tab.Screen name="create" component={CreateYCTV} />
<Tab.Screen
name="notification"
component={Notification}
options={{
title: 'Thông báo',
tabBarBadge: '13',
}}
/>
<Tab.Screen
name="manager"
component={Manager}
options={{
title: 'Quản lý',
}}
/>
</Tab.Navigator>
);
};
const mapStateToProps = (state, ownProps) => {
return {
isShowModalCreate: state.loading.isShowModalCreate,
};
};
const mapDispatch = {
showModalCreate: showModalCreate,
hideModalCreate: hideModalCreate,
};
export default connect(mapStateToProps, mapDispatch)(Tabbar);
I am trying deeplinking in React-Native. The code works properly when the app is in the background. But once I remove the app from background and try to launch it using the link in safari. The app is launched with details screen. But I could not find previous (Home) screens in the Navigation Stack. Please find the code below:
/* eslint-disable react-native/no-inline-styles */
import 'react-native-gesture-handler';
import React from 'react';
import {TouchableOpacity, Text, View} from 'react-native';
import {useLinking, NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
const HomeScreen = ({navigation}) => {
return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}>
<Text>Home Screen</Text>
<TouchableOpacity
onPress={() => {
navigation.navigate('Details', {itemId: 40});
}}>
<Text>Go to Details</Text>
</TouchableOpacity>
</View>
);
};
const DetailScreen = ({route, navigation}) => {
return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}>
<Text>Details Screen</Text>
<Text>Item Id: {route.params.itemId}</Text>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Text>Go Back</Text>
</TouchableOpacity>
</View>
);
};
const Stack = createStackNavigator();
const App = () => {
const ref = React.useRef();
const {getInitialState} = useLinking(ref, {
prefixes: ['deeplink://'],
config: {
initialRouteName: 'Home',
Home: 'Home',
Details: {
path: 'Details/:itemId',
parse: {
itemId: null,
},
},
},
getPathFromState(state, config) {
console.log(state);
},
});
const [isReady, setIsReady] = React.useState(false);
const [initialState, setInitialState] = React.useState();
React.useEffect(() => {
Promise.race([
getInitialState(),
new Promise((resolve) => setTimeout(resolve, 150)),
])
.catch((e) => {
console.error(e);
})
.then((state) => {
if (state !== undefined) {
setInitialState(state);
}
setIsReady(true);
});
}, [getInitialState]);
if (!isReady) {
return null;
}
return (
<NavigationContainer
fallback={<Text>Loading...</Text>}
initialState={initialState}
ref={ref}>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
Launched the app using "deeplink://Details/86" in Safari.
First, update to latest version of #react-navigation/native and then follow the linking docs: https://reactnavigation.org/docs/configuring-links/
Instead of useLinking, you can pass a linking prop to the NavigationContainer component. Then change your config to following:
const App = () => {
const linking = {
prefixes: ["deeplink://"],
config: {
initialRouteName: "Home",
screens: {
Home: {
path: "home",
},
Details: {
path: "details/:itemId"
}
}
}
};
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
Then you can open links like deeplink://home or deeplink://details/someid.
I am implementing react-navigation-drawer from React Navigation Library. But facing problem related to header. The header bar is not showing in any of the screens.
This is my App.js
import React from "react";
import { StyleSheet, ScrollView, View } from "react-native";
//import DrawerNavigator from "./navigation/DrawerNavigator";
import { Platform, Dimensions } from "react-native";
import { createAppContainer } from "react-navigation";
import { createDrawerNavigator } from "react-navigation-drawer";
import Home from "./components/home";
import Contact from "./components/contact";
const WIDTH = Dimensions.get("window").width;
const RouteConfigs = {
Home: {
screen: Home
},
Contact: {
screen: Contact
}
};
const DrawerNavigatorConfig = {
drawerWidth: WIDTH * 0.75,
drawerType: "both",
initialRouteName: "Home"
};
const DrawerNavigator = createDrawerNavigator(
RouteConfigs,
DrawerNavigatorConfig
);
const MyApp = createAppContainer(DrawerNavigator);
export default class App extends React.Component {
render() {
return <MyApp />;
}
}
And this is my home screen
import React, { Component } from "react";
import { View, Image, Text, StyleSheet, ScrollView } from "react-native";
import { FontAwesomeIcon } from "#fortawesome/react-native-fontawesome";
import { faTruck, faHome } from "#fortawesome/free-solid-svg-icons";
class Home extends Component {
static navigationOptions = {
headerTitle: "Home",
drawerIcon: ({ tintColor }) => <FontAwesomeIcon size={25} icon={faHome} />
};
render() {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Home Screen</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#F5F5F5",
flexDirection: "column"
},
icon: {
width: 24,
height: 24
}
});
export default Home;
Can anyone help me. Thanks in advance!!!
#hongdeveloper this is a simple example solution for react navigation 5:
function Root() {
return (
<Stack.Navigator>
<Stack.Screen options={{title: "Profile"}} name="Profile" component={Profile} />
<Stack.Screen options={{title: "Settings"}} name="Settings" component={Settings} />
</Stack.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name="Home" component={Home} />
<Drawer.Screen name="Root" component={Root} />
</Drawer.Navigator>
</NavigationContainer>
);
}
You can find about the navigation to a screen in a nested navigator in docs and you can try this example on Snack
The drawer navigator does not contain headers. Stack navigators must be configured to display headers.
const DrawerNavigator = createDrawerNavigator(
RouteConfigs,
DrawerNavigatorConfig
);
const Root = createStackNavigator({
Main: { screen : DrawerNavigator}
},
{
defaultNavigationOptions : ({ navigation }) => ({
title: "Screen"
})
})
const Stacks = createAppContainer(Root)
export default Stacks;
Since December 2020 you can now use the headerShown: true setting in screenOptions of your Drawer.Navigator to show the header in React Navigation 5.
See more about this issue here: https://github.com/react-navigation/react-navigation/issues/1632
See the commit and comments about the new feature in React Navigation 5 here
https://github.com/react-navigation/react-navigation/commit/dbe961ba5bb243e8da4d889c3c7dd6ed1de287c4
Late reply, But I did it with the below code.
I created separate stack navigators for each screen and after that added all the stack navigators in the drawer navigator.
The good thing is it is fully customized.
Please see my code below.
const WIDTH = Dimensions.get('window').width;
const HomeNavigator = createStackNavigator(
{
Home: Home
},
{
defaultNavigationOptions: ({ navigation }) => {
return {
headerStyle: {
backgroundColor: '#1e89f4'
},
headerTitle: 'Knowledge Woledge',
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
textAlign: 'center',
flex: 1
},
headerLeft: (
<View style={{ paddingLeft: 13 }}>
<FontAwesomeIcon
size={25}
color='#fff'
icon={faBars}
onPress={() => navigation.openDrawer()}
/>
</View>
),
headerRight: <View />
};
}
}
);
const DetailNavigator = createStackNavigator(
{
PostDetail: PostDetail
},
{
defaultNavigationOptions: ({ navigation }) => {
return {
headerStyle: {
backgroundColor: '#1e89f4'
},
headerTitle: () => {
return (
<Text
style={{
color: '#fff',
fontWeight: 'bold',
textAlign: 'center',
flex: 1,
fontSize: 20
}}
>
{navigation.getParam('headerTitle')}
</Text>
);
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
textAlign: 'center',
flex: 1
},
headerLeft: (
<View style={{ paddingLeft: 13 }}>
<FontAwesomeIcon
size={25}
color='#fff'
icon={faArrowLeft}
onPress={() => navigation.goBack(null)}
/>
</View>
),
headerRight: <View />
};
}
}
);
Assigned this in a const
const RouteConfigs = {
Home: {
screen: HomeNavigator,
navigationOptions: {
drawerLabel: 'Home',
drawerIcon: ({ tintColor }) => (
<FontAwesomeIcon size={20} color={tintColor} icon={faHome} />
)
}
},
Detail: {
screen: DetailNavigator,
navigationOptions: {
drawerLabel: () => {
return null;
}
}
}
};
And finally, create a drawer navigator with this.
const DrawerNavigatorConfig = {
drawerWidth: WIDTH * 0.75,
drawerType: 'both',
initialRouteName: 'Home'
};
const DrawerNavigator = createDrawerNavigator(
RouteConfigs,
DrawerNavigatorConfig
);
const Stacks = createAppContainer(DrawerNavigator);
export default Stacks;