I've watched tutorials and looked at the docs however I still don't get it, I need someone to explain using my situation/code.
My navigation setup is similar to Instagram. Bottom tab navigation has Home/Search/Inventory/Profile.
Here is my navigation file:
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { PresenceTransition, Text, View } from "native-base";
import Home from "../../screens/Home";
import Profile from "../../screens/Profile";
import InventoryIcon from "../svg/InventoryIcon";
import SearchIcon from "../svg/SearchIcon";
import UserIcon from "../svg/UserIcon";
import HomeIcon from "../svg/HomeIcon";
import Search from "../../screens/Search";
import Inventory from "../../screens/Inventory";
import authStore from "../../zustand/authStore";
import Login from "../../screens/Login/Login";
import { SafeAreaView } from "react-native-safe-area-context";
import { TouchableOpacity } from "react-native";
import userDataStore from "../../zustand/userDataStore";
import Avatar from "../Avatar";
const Tab = createBottomTabNavigator();
export default function BottomNavigation() {
const currentUser = authStore((state) => state.currentUser);
if (!currentUser)
return (
<SafeAreaView>
<Login />
</SafeAreaView>
);
return (
<Tab.Navigator
tabBar={(props) => <TabBar {...props} />}
initialRouteName="Home"
screenOptions={({ route }) => ({
headerShown: false,
tabBarStyle: {
elevation: 0,
borderTopWidth: 0,
backgroundColor: "#2563eb",
},
tabBarShowLabel: false,
})}
>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Search" component={Search} />
<Tab.Screen name="Inventory" component={Inventory} />
<Tab.Screen name="Profile" component={Profile} />
</Tab.Navigator>
);
}
function TabBar({ state, descriptors, navigation }) {
const currentUser = authStore((state) => state.currentUser);
const userData = userDataStore((state) => state.userData);
return (
<View className="bg-blue-600 py-4" style={{ flexDirection: "row" }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const isFocused = state.index === index;
let params;
if (route.name === "Profile") {
params = {
uid: currentUser?.uid,
};
}
const onPress = () => {
const event = navigation.emit({
type: "tabPress",
target: route.key,
});
if (route.name === "Profile" && !event.defaultPrevented) {
navigation.navigate(route.name, params);
return;
}
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name, params);
}
};
const onLongPress = () => {
navigation.emit({
type: "tabLongPress",
target: route.key,
});
};
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityState={isFocused ? { selected: true } : {}}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1 }}
>
<View className="relative flex w-full flex-1 items-center justify-center">
<TabIcon
currentUserProfilePicture={userData?.profilePicture}
name={route.name}
/>
<PresenceTransition
visible={isFocused}
initial={{ opacity: 0, scale: 0 }}
animate={{
opacity: 1,
scale: 1,
transition: {
duration: 200,
},
}}
className="absolute -bottom-7 h-4 w-4 rounded-sm bg-white"
/>
</View>
</TouchableOpacity>
);
})}
</View>
);
}
function TabIcon({
name,
currentUserProfilePicture,
}: {
name: string;
currentUserProfilePicture: undefined | null | string;
}) {
if (name === "Home") {
return <HomeIcon svgClassName="text-white w-6 h-6" />;
}
if (name === "Search") {
return <SearchIcon svgClassName="text-white w-6 h-6" />;
}
if (name === "Collections") {
return <CollectionsIcon svgClassName="text-white w-6 h-6" />;
}
if (name === "Profile") {
return currentUserProfilePicture ? (
<Avatar
url={currentUserProfilePicture}
alt="Your profile"
className="h-6 w-6"
/>
) : (
<UserIcon svgClassName="text-white w-6 h-6" />
);
}
return null;
}
Now, the problem is I want to add a Settings
screen. This screen shouldn't be in the bottom tab bar, it should only be navigated to from the Profile
screen. If I add it to the navigation, it is automatically added.
I could do something like:
const hiddenRoutes = ["Settings"]
if (hiddenRoutes.includes(route.name) {
return;
}
But this seems quite hacky to me, I feel its wrong.
How should I best declare routes, but keep them out of the bottom tab navigation?
You will want to create another navigation layer for your profile screen
To make the stack navigation for the profile screen:
const ProfileStack = createNativeStackNavigator<ProfileStackParamList>();
function ProfileNavigator() {
return (
<ProfileStack.Navigator >
<ProfileStack.Screen
name={"Profile"}
component={Profile}
options={() => ({
title: "Profile",
})}
/>
<ProfileStack.Screen
name={"Settings"}
component={Settings}
options={() => ({
title: "Settings",
})}
/>
</ProfileStack.Navigator>
); }
Then in where you are making your Tab navigator use this stack navigation in the component
<Tab.Screen name="Profile" component={ProfileNavigator} />