React Native Nested Stack Inside BottomTabNavigator issue - reactjs

The navigation process I want to achieve is (BottomTabNavigator -> RoutinesStackNavigator -> Routines) which I'm using a stack navigator nested inside a bottom tab navigator
When I navigate to the RoutinesStackNavigator it isn't loading the Routines page and I am not sure why.
BottomTabNavigation.tsx
export default function Navigation() {
return (
<>
<SoundProvider>
<TimerProvider>
<NavigationContainer>
<Tab.Navigator
sceneContainerStyle={{ backgroundColor: COLORS.white }}
initialRouteName="Pomodoro"
screenOptions={({ route, navigation }) => ({
tabBarShowLabel: false,
headerShown: false,
tabBarStyle: [styles.tabBar, styles.tabBarBoxShadow],
tabBarIcon: ({ focused }) => {
let rn = route.name;
let iconTag;
// assigning navigation icon based on name of route
if (rn === pomodoroName) {
// assigning JSX Tag
iconTag = (
<PomodoroIcon
size={iconSize}
fillColor={iconColor}
isFocused={focused}
/>
);
} else if (rn === agendaName) {
iconTag = (
<AgendaIcon
size={iconSize}
fillColor={iconColor}
isFocused={focused}
/>
);
} else if (rn === tutorialsName) {
iconTag = (
<TutorialsIcon
size={iconSize}
fillColor={iconColor}
isFocused={focused}
/>
);
} else if (rn === routinesStackNavigatorName) {
iconTag = (
<RoutinesIcon
size={iconSize}
fillColor={iconColor}
isFocused={focused}
/>
);
}
return (
<Pressable
onPress={() => {
// add haptic feedback and navigate to new route
addLightHapticFeedback("light");
navigation.navigate(rn);
}}
>
{iconTag}
</Pressable>
);
},
})}
>
<Tab.Screen name={pomodoroName} component={Pomodoro} />
<Tab.Screen name={agendaName} component={Agenda} />
<Tab.Screen name={tutorialsName} component={Tutorials} />
<Tab.Screen
name={routinesStackNavigatorName}
component={RoutinesStackNavigator}
/>
</Tab.Navigator>
</NavigationContainer>
</TimerProvider>
</SoundProvider>
</>
);
}
RoutinesStackNavigator.tsx
export default function RoutinesStackNavigator() {
const Stack = createStackNavigator();
return (
<>
<SafeAreaView>
<Stack.Navigator initialRouteName="Routines">
<Stack.Screen name="Routines" component={Routines} />
<Stack.Screen name="CreateRoutine" component={CreateRoutine} />
</Stack.Navigator>
</SafeAreaView>
</>
);
}
index.Routines.tsx
export default function Routines() {
const { width } = useWindowDimensions();
return (
<>
<FocusedStatusBar barStyle="light-content" />
<View style={styles.container}>
<View style={styles.row}>
<Text style={styles.title}>{STRINGS.routinesTitle}</Text>
<TouchableOpacity onPress={() => {}}>
<AntDesign name="pluscircle" size={44} color={COLORS.white} />
</TouchableOpacity>
</View>
<View style={[styles.main, { width: width }]}>
<View style={{ height: "72%" }}>
<ScrollView
showsVerticalScrollIndicator={true}
style={{ marginTop: 10, marginBottom: 10 }}
>
<RoutineCard />
<RoutineCard />
</ScrollView>
</View>
<TouchableOpacity style={styles.button} onPress={() => {}}>
<Text style={styles.buttonText}>Start Session</Text>
</TouchableOpacity>
</View>
</View>
</>
);
}
none of the Routines component renders, except the StatusBar color changes. So the component must be rendering but the rest of the content doesn't show up.

You have to remove <SafeAreaView> from RoutinesStackNavigator.tsx
const Stack = createStackNavigator();
export default function RoutinesStackNavigator() {
return (
<Stack.Navigator initialRouteName="Routines">
<Stack.Screen name="Routines" component={Routines} />
<Stack.Screen name="CreateRoutine" component={CreateRoutine} />
</Stack.Navigator>
);
}

Related

goBack not working in nested navigators react native. TabNavigator and StackNavigator

I have this problem several days ago and I can't find a solution.
This is my navigation structure
Index.tsx -->
<NavigationContainer>
<Stack.Navigator initialRouteName='tabsBottomHome'
screenOptions={{
headerTitle: () => <Header />,
headerBackground: () => <HeaderBackground />,
headerLeft: ({ onPress, canGoBack }) =>
canGoBack ? (
<Pressable onPress={onPress} style={{ width: 30, height: 15 }}>
<ArrowLeft height={15} width={15} style={{ marginLeft: 15 }} />
</Pressable>
) : (
<Pressable style={{ width: 65, height: 35 }}></Pressable>
),
headerRight: () => <HeaderRight />,
}}
>
{state.isSignIn ? (
<>
<Stack.Screen
name='tabsBottomHome'
component={TabsBottomHome}
options={{headerShown: false }}
/>
</>
) : (
<>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="LoginError"
component={LoginError}
/>
</>
)}
</Stack.Navigator>
</NavigationContainer>
This would be the first navigation of my application. So far everything is going well.
TabsBottomNavigation -->
<Tab.Navigator
initialRouteName='Home'
screenOptions={{
headerShown:true,
tabBarShowLabel:false,
tabBarStyle:style.tabsBottomContainer,
unmountOnBlur: true,
headerTitle: () => <Header />,
headerBackground: () => <HeaderBackground />,
headerLeft: () =>
routeName != "Home" ? (
<Pressable onPress={navigateBack} style={{ width: 65, height: 15 }}>
<ArrowLeft height={15} width={15} style={{ marginLeft: 15 }} />
</Pressable>
)
: (
<Pressable style={{ width: 65, height: 35 }}></Pressable>
)
,
headerRight: () => <HeaderRight />,
}}
sceneContainerStyle={style.backgroundContent}
screenListeners={({route})=>({
state: ()=> {
setRouteName(route.name);
}
})}
backBehavior={'history'}
>
<Tab.Screen
name='Assets'
component={AssetScreen}
options={{tabBarIcon:({focused,color})=>(
<View>
<Image
source={iconAsset}
style={{
tintColor: focused ? '#00B2DF' : '',
marginTop: 8
}}
/>
</View>
)}} />
<Tab.Screen
name='GatewayStack'
component={ConnectGatewayStack}
options={{tabBarIcon:({focused,color})=>(
<View>
<Image
source={iconBluetooth}
style={{
tintColor: focused ? '#00B2DF' : ''
}}
/>
</View>
)}} />
<Tab.Screen
name='Home'
component={HomeScreen}
options={{tabBarIcon:({focused,color})=>(
<View>
<Image
source={iconHome}
style={{
tintColor: focused ? '#00B2DF' : ''
}}
/>
</View>
)
}} />
</Tab.Navigator>
everything is fine here too, the conflict is in the ConnectGatewayStack stack
ConnectGatewayStack -->
export type StackConnectList = {
Connect: undefined;
QRScan: undefined;
GatewayList: undefined;
GatewayInfo: undefined;
NotFoundGateway: undefined;
GatewayDetected: undefined;
ErrorConnecting: undefined;
}
const GatewayStack = createStackNavigator<StackConnectList>();
const ConnectGatewayStack = () =>{
return (
<GatewayStack.Navigator initialRouteName='Connect'
screenOptions={{headerShown:false}}
>
<GatewayStack.Screen name='Connect' component={ConnectScreen} />
<GatewayStack.Screen name='GatewayList' component={GatewayList} />
<GatewayStack.Screen name='GatewayInfo' component={GatewayInfo} />
<GatewayStack.Screen name='QRScan' component={QRScanScreen} />
<GatewayStack.Screen name='NotFoundGateway' component={NotFoundGateway} />
<GatewayStack.Screen name='GatewayDetected' component={GatewayDetected} />
<GatewayStack.Screen name='ErrorConnecting' component={ErrorConnecting} />
</GatewayStack.Navigator>
)
}
export {ConnectGatewayStack};
When I navigate to the ConnectGatewayStack tab, which contains the other components that I just showed, if I go to a child component and press the back button in header, it returns directly to the TabsBottomHome (that is, to the Tabs navigation) and not to the component that was previously visited.
For the navigation back, i use
import { useNavigation } from '#react-navigation/core';
whit
const navigation = useNavigation();
navigation.goback();
I tried whit diferents methods, but none solved my problem.
example test whit
backBehavior={'history'}
backBehavior={'order'}
and many functions of the core react-native and react-native-navigation
any ideas?
sorry for my bad english and thank you very much

Can I disable one of the tabs in the bottomtabnavigator? React Native

I have used react navigation's createBottomTabNavigator.
As can be seen in the code below, if it is in !this.props.isLoading status, it is directed to Cart or Payment tabs. I had a problem here, if there are very fast transitions between tabs, there may be incorrect returns from API requests.
That's why I want to disable the tabs for a short time, but I could not succeed. Is there anyone who can help with this?
const Tab = createBottomTabNavigator();
</Tab.Navigator>
initialRouteName="Home"
screenOptions={({route}) => ({
tabBarVisible: this.props.isTabBarVisible,
unmountOnBlur: true,
tabBarIcon: ({focused, color, size}) => {
let iconName;
switch (route.name) {
case 'Home':
return (
<TouchableWithoutFeedback
onPress={() => {
this.props.navigation.navigate('Home');
this.props.navigation.canGoBack();
}}>
<View style={styles.Tabs__iconContainer}>
<Icon
type="ionicon"
name={'home'}
color={color}
size={size - 4}
/>
</View>
</TouchableWithoutFeedback>
);
case 'Customer':
return (
<View style={styles.Tabs__iconContainer}>
<Icon
type="ionicon"
name={'person'}
color={color}
size={size - 4}
/>
{this.customerCount ? (
<Badge
value={this.customerCount}
textStyle={styles.Tabs__customerTickTextStyle}
status={'success'}
containerStyle={styles.Tabs__badgeContainer}
badgeStyle={styles.Tabs__customerBadgeStyle}
/>
) : null}
</View>
);
case 'Cart':
let totalProductCount = this.props.productList.reduce(
(x, y) => x + y.quantity,
0,
);
return (
<TouchableWithoutFeedback
onPress={() => {
if (!this.props.isLoading) {
setTimeout(() => {
this.props.navigation.navigate('Cart');
}, 100);
}
}}>
<View
style={[styles.Tabs__iconContainer, {paddingTop: '11%'}]}>
<Icon
type="ionicon"
name={'basket'}
color={color}
size={size + 1}
/>
{totalProductCount > 0 ? (
<Badge
value={totalProductCount}
textStyle={
totalProductCount < 10
? styles.Tabs__basketAmountLow
: totalProductCount < 100
? styles.Tabs__basketAmountMedium
: totalProductCount < 1000
? styles.Tabs__basketAmountHigh
: styles.Tabs__basketAmountHighest
}
status={'success'}
containerStyle={styles.Tabs__badgeContainer}
badgeStyle={styles.Tabs__basketBadgeStyle}
/>
) : null}
</View>
</TouchableWithoutFeedback>
);
case 'Payment':
return (
<TouchableWithoutFeedback
onPress={() => {
if (!this.props.isLoading) {
setTimeout(() => {
this.props.navigation.navigate('Payment');
}, 100);
}
}}>
<View
style={[styles.Tabs__iconContainer, {paddingTop: '11%'}]}>
<Icon
type="ionicon"
name={'card-outline'}
color={color}
size={size + 1}
/>
</View>
</TouchableWithoutFeedback>
);
}
return (
<Icon
style={{paddingTop: 12}}
type="ionicon"
name={iconName}
color={color}
size={size}
/>
);
},
title: '',
})}
................
<Tab.Screen name="Cart" component={Cart} />
{configuration.paymentProviders.length > 0 && (
<Tab.Screen name="Payment" component={Payment} />
)}
</Tab.Navigator>
This might not be the cleanest answer, but I would recommend adding a state to help with it.
const [loading, setLoading] = useState(false);
Then put a conditional statement in every Tab where you want to disable its onPress, in the following example, I added !loading to the if so it won't trigger another navigation when clicked.
case 'Payment':
return (
<TouchableWithoutFeedback
onPress={() => {
if (!this.props.isLoading && !loading) {
setLoading(true);
setTimeout(() => {
setLoading(false);
this.props.navigation.navigate('Payment');
}, 100);
}
}}>
<View
style={[styles.Tabs__iconContainer, {paddingTop: '11%'}]}>
<Icon
type="ionicon"
name={'card-outline'}
color={color}
size={size + 1}
/>
</View>
</TouchableWithoutFeedback>
);

Navigation with parameters from custom element in Flatlist in React Native: Empty parameters

After not succeeding in solving my problem here, I had to create a new question with all my classes in it.
I am new to react native and have a problem figuring out how to navigate from one class to another one with passing parameters and would appreciate your help.
All I want to do is:
SessionCreate with flatlist containing CustomButton
Navigate from SessionCreate to ItemConfig by clicking CustomButton
Pass parameter "element" to ItemConfig
Show content of parameter passed in ItemConfig
With this setup an empty "element" is passed as parameter to the ItemConfigScreen (but no error occurs):
app.js:
//Views
function Home({ navigation }) {
return (
<HomeScreen />
);
}
function Session({ navigation }) {
return (
<SessionScreen />
);
}
//subviews
function SessionCreate({ navigation }) {
return (
<SessionCreateScreen />
);
}
function ItemConfig({ navigation }) {
return (
<ItemConfigScreen />
);
}
//navigation stacks
const SessionStack = createStackNavigator();
function SessionStackScreen({ navigation }) {
return (
<SessionStack.Navigator>
<SessionStack.Screen
name="Session"
component={Session}
options={{ tabBarLabel: 'Session!' }}
/>
<SessionStack.Screen
name="SessionCreate"
component={SessionCreate}
options={{ tabBarLabel: 'SessionCreate!' }}
/>
<SessionStack.Screen
name="ItemConfig"
component={ItemConfig}
options={{ tabBarLabel: 'ItemConfig!' }}
/>
</SessionStack.Navigator>
);
}
//Navbar Bottom
const Tab = createBottomTabNavigator();
function App() {
return (
<View style={[theme.colcontainer, { flexDirection: "column" }]} >
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home-outline';
} else if (route.name === 'Session') {
iconName = focused ? 'book' : 'book-outline';
}
// dynamic ionicon
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Session" component={SessionStackScreen} />
</Tab.Navigator>
</NavigationContainer>
</View >
);
}
export default App;
SessionScreen.js:
function SessionScreen() {
const navigation = useNavigation();
return (
<View style={[theme.container, { flexDirection: "column" }]} >
<View>
<TouchableOpacity onPress={() => navigation.navigate('SessionCreate')}>
<Text >Create Session</Text>
</TouchableOpacity>
</View>
</View >
);
}
export default SessionScreen;
SessionCreateScreen.js:
//data
const sessionElements = [
{
id: "1",
title: "title1"
}
];
function SessionCreateScreen() {
const navigation = useNavigation()
const renderItemConfiguration = ({ item }) => (
<CustomButton element={item.title} onPress={() => navigation.navigate('ItemConfig', { element: 'item.title' })} />
);
return (
<View style={{ flexDirection: "column", flex: 2}} >
<SafeAreaView >
<FlatList
data={sessionElements}
renderItem={renderItemConfiguration}
keyExtractor={(item) => item.id}
/>
</SafeAreaView>
</View >
);
}
export default SessionCreateScreen;
ItemConfigScreen.js:
const element = "";
function ItemConfigScreen() {
return (
<ScrollView >
<View style={{ flexDirection: "column", flex: 2}} >
<Text>Configure {element} here</Text>
</View >
</ScrollView>
);
}
export default ItemConfigScreen;
Any help is appreciated.
To get parameters in ItemConfigScreen you have to use the useRoute hook from the react-navigation package.
you can read more about it here useRoute
import {useRoute} from '#react-navigation/native';
function ItemConfigScreen() {
const route = useRoute();
const element = route.params?.element;
return (
<ScrollView >
<View style={{ flexDirection: "column", flex: 2}} >
<Text>Configure {element} here</Text>
</View >
</ScrollView>
);
}
There is also a mistake in your onPress navigation call in CustomButton, instead of 'item.title' you will have to pass ${item.title} then only actual data will be passed. JS Template Literals
<CustomButton element={item.title} onPress={() => navigation.navigate('ItemConfig', { element: `${item.title}` })} />

How to add multiple divider component in a react navigation drawer

I have a drawer navigation, what I wanted to happen is add divider separating the drawer icon and texts, but I get an error instead
Error is: A navigator can only contain screen components...
Here is my approach:
function DrawerNav(): React$Element<{}> {
return (
<Drawer.Navigator
drawerStyle={styles.drawerStyle}
drawerContentOptions={{
activeTintColor: 'white',
inactiveTintColor: 'white',
activeBackgroundColor: '#163E69',
inactiveBackgroundColor: '#02152B'
}}>
<Drawer.Screen
name="Dashboard"
component={Dashboard}
options={{
drawerIcon: () => (
<Image source = {"assets/icons/dashboard.png"} />
),
}}
/>
<DrawerDivider label="Account" /> // This is my divider component
<Drawer.Screen
name="Profile"
component={Profile}
options={{
drawerIcon: () => (
<Image source={'assets/icons/profile.png'} />
),
}}
/>
<DrawerDivider label="Actions" /> // also my divider component
<Drawer.Screen
name="Settings"
component={Settings}
options={{
drawerIcon: () => (
<Image source={'assets/icons/settings.png'} />
),
}}
/>
<DrawerDivider label="Actions" /> // also my divider component
<Drawer.Screen
name="Logout"
component={Logout}
options={{
drawerIcon: () => (
<Image source={'assets/icons/logout.png'} />
),
}}
/>
</Drawer.Navigator>
);
}
Any suggestions/help are appreciated.
this how I implemented it in my project , you need to create a custom drawerContent component , drawerContent gives incredible amount of flexibility you can literally create what ever you want with it .
Navigator :
import DrawerContent from './DrawerContent'
import { createDrawerNavigator } from '#react-navigation/drawer';
const Navigator =({authenticated,userType})=>{
const Drawer = createDrawerNavigator();
return <Drawer.Navigator
screenOptions={{headerShown:false}}
initialRouteName="INTIALROUTE"
drawerContent={props => <DrawerContent {...props} />}
>
<Drawer.Screen name="route1" component={Screen1} />
<Drawer.Screen name="route1" component={Screen2} />
</Drawer.Navigator>
}
the custom drawerContent component :
import React from 'react'
import {StyleSheet,View,Text,TouchableOpacity} from 'react-native'
import {DrawerContentScrollView,DrawerItem} from '#react-navigation/drawer';
const DrawerContent=(props)=> {
const {navigation}=props
const naviagetToRoute=(e)=>{
navigation.navigate(route)
}
//here you can return diffrent drawerContent based on auth state for example
if(isAuthenticated){
//isAuthenticated can be passed through props using redux's connect or context api
return <View style={{flex:1}}>
<DrawerContentScrollView {...props}>
<TouchableOpacity onPress={naviagetToRoute}>
<View style={styles.drawerItem}>
<Image source = {"assets/icons/logout.png"} />
<Text style={{color:colors.BLACK}} >Logout</Text>
</View>
</TouchableOpacity>
</DrawerContentScrollView>
</View>
}
return (
<View style={{flex:1}}>
<DrawerContentScrollView {...props}>
<TouchableOpacity onPress={naviagetToRoute}>
<View style={styles.drawerItem}>
<Image source = {"assets/icons/dashboard.png"} />
<Text style={{color:colors.BLACK}} >DashBoard</Text>
</View>
</TouchableOpacity>
</DrawerContentScrollView>
</View>
)
}
export default DrawerContent
const styles = StyleSheet.create({
drawerItem: {
display :'flex',
flexDirection:'row',
alignItems:'center',
justifyContent:"space-between",
padding:16,
elevation:12,
backgroundColor:'#fff',
borderRadius:12,
marginBottom:16,
flex:1
}
});

React Native BottomTabNavigator navigation not working

I created a BottomTabNavigator app with expose, it's my first project with react-native. On the last tab I have a FlatList with a few elements which should open different Views outside the TabNavigation. But when I click on a item in the ListView, I get the following error: TypeError: undefined is not an object (evaluating 'item.navigation.navigate'. This also happens when I try to do it with props, but props also won't work properly in my project.
I'm trying to fix this for two days and I don't know any further. Can anyone please help me with this? In addition, has someone a good guide for navigation and props?
App.tsx
export default function App() {
const isLoadingComplete = useCachedResources();
const colorScheme = useColorScheme();
if (!isLoadingComplete) {
return null;
} else {
return (
<SafeAreaProvider>
<Navigation colorScheme={colorScheme} />
<StatusBar />
</SafeAreaProvider>
);
}
}
index.tsx
export default function Navigation({ colorScheme }: { colorScheme: ColorSchemeName }) {
return (
<NavigationContainer
linking={LinkingConfiguration}
theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<RootNavigator />
</NavigationContainer>
);
}
const Stack = createStackNavigator<RootStackParamList>();
function RootNavigator() {
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Root" component={BottomTabNavigator} />
<Stack.Screen name="WebViewImprint" component={WebViewImprint} />
<Stack.Screen name="MenuFlatList" component={MenuFlatList} />
<Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }} />
</Stack.Navigator>
);
}
BottomTabNavigator.tsx
const BottomTab = createBottomTabNavigator<BottomTabParamList>();
export default function BottomTabNavigator() {
const colorScheme = useColorScheme();
return (
{ .... }
<BottomTab.Screen
name="TabMenu"
component={TabMenuNavigator}
options={{
tabBarLabel: "MenĂ¼",
tabBarIcon: ({ color }) => <TabBarIcon name="ios-code" color={color} />,
}}
/>
</BottomTab.Navigator>
);
}
function TabBarIcon(props: { name: string; color: string }) {
return <Ionicons size={30} style={{ marginBottom: -3 }} {...props} />;
}
const TabMenuStack = createStackNavigator<TabMenuParamList>();
function TabMenuNavigator() {
const colorScheme = useColorScheme();
return (
<TabMenuStack.Navigator>
<TabMenuStack.Screen
name="TabMenu"
component={TabMenu}
options={{
header: props => <StacknavigatorImage />,
headerStyle: {
backgroundColor: Colors[colorScheme].barBackgroundColor
}
}}
/>
</TabMenuStack.Navigator>
);
}
MenuFlatListComponent.tsx
const Item = ({ title } : {title: any}) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
export default class MenuFlatList extends React.Component {
render(){
return (
<SafeAreaView style={styles.container}>
<FlatList style={styles.flatList}
data={DATA}
renderItem={({item} : {item: any}) => {
return (
<TouchableWithoutFeedback onPress={() => {
item.navigation.navigate('WebViewImprint');
}}>
<View style={styles.item}>
<Image source={item.icon} style={styles.icon} />
<Item title={item.title} />
</View>
</TouchableWithoutFeedback>
);}
}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
}
You are trying to access navigation which is passed to the MenuFlatList component as a prop you should do like below
<TouchableWithoutFeedback onPress={() => {
this.props.navigation.navigate('WebViewImprint');
}}>
Any screen component in the navigation will receive a 'navigation' props which you can use to navigate to other screens.

Resources