I'm trying to make a tab bar like in the picture bellow, using React Navigation.
I tried some codes, but nothing worked. Some codes are from previous React Navigation Version. The really problem is make the tab bar have a margin from from bottom and both sides, and borders rounded.
Anyone can help me please?!
here is demo: https://snack.expo.io/#nomi9995/createbottomtabnavigator-%7C-react-navigation
you can use tabBar props to make custom TABBAR
<NavigationContainer>
<Tab.Navigator tabBar={props => <MyTabBar {...props} />}>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
MyTabBar Component
function MyTabBar({ state, descriptors, navigation }) {
return (
<View style={{ flexDirection: 'row',backgroundColor:"#F4AF5F",height:50,borderRadius:50,justifyContent:"center",alignItems:"center" }}>
{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 onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1, alignItems:"center" }}
>
<Text style={{ color: isFocused ? '#673ab7' : '#222' }}>
{label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
The key to this is to add the style position: 'absolute' to the outer <View> container on your custom TabBar. This will get rid of the white background issue.
Related
I have a simple expo managed React Native project setup with react-navigation
I have 2 screens one of which is the Home screen and the other is a screen for QRCode scanner which uses expo's BarCodeScanner.
The issue here is that when navigating from the Home screen to the QRCode screen and back to the Home screen keeps the camera alive. I can see the activity in the status bar saying 'Expo Go is using your camera'
I tried various ways to tackle this,
Passing the screen as a render prop to Stack.Screen so it mounts every time we navigate, but still the same issue
Tried using the isFocused method to conditionally render the component but still no luck
<NavigationContainer fallback={<Text>Loading...</Text>}>
<Stack.Navigator
screenOptions={({ route, navigation }) => ({
headerShadowVisible: false,
headerTitle: () => (
<Text
style={{
fontSize: 30,
fontFamily: Font["900"],
}}
>
Test
</Text>
),
headerRight: () =>
route.name !== "QR" ? (
<Pressable onPress={() => navigation.navigate("QR")}>
<QrcodeIcon size={26} color="black" />
</Pressable>
) : null,
})}
>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="QR" children={() => <QRCode />} />
</Stack.Navigator>
</NavigationContainer>
And the code for the QRCode component looks like the following:
const QRCode = () => {
const [hasPermission, setHasPermission] = useState<boolean>();
const [scanned, setScanned] = useState<boolean>(false);
const isFocused = useIsFocused();
const linkTo = useLinkTo();
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === "granted");
})();
}, []);
const handleBarCodeScanned = ({ type, data }: BarCodeEvent) => {
setScanned(true);
linkTo(data);
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View style={styles.container}>
{isFocused ? (
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFill}
/>
) : null}
</View>
);
};
How to implement a buttom navigator that containt a button that handle DrawerNav display
To be able to do it, first you would have to add your custom tab bar, by overriding the tabBar property on the Tab.Navigator component. Here is an example from the official React Navigation docs.
Then you would have to add a button to your custom tab bar, which purpose would be to open the drawer screen (navigation.openDrawer).
Here's an example how you could do it:
import React from 'react';
import {
Button,
SafeAreaView,
Text,
TouchableOpacity,
View,
} from 'react-native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { createDrawerNavigator } from '#react-navigation/drawer';
const Tab = createBottomTabNavigator();
const TabItem = ({ label, onPress, isFocused = false }) => (
<TouchableOpacity
accessibilityRole="button"
onPress={onPress}
style={{
flex: 1,
padding: 20,
alignItems: 'center',
justifyContent: 'center',
}}>
<Text style={{ color: isFocused ? '#673ab7' : '#222' }}>{label}</Text>
</TouchableOpacity>
);
const MyTabBar = ({ state, descriptors, navigation }) => {
const focusedOptions = descriptors[state.routes[state.index].key].options;
if (focusedOptions.tabBarVisible === false) {
return null;
}
const onOpenDrawerPress = () => {
navigation.openDrawer();
};
return (
<View style={{ flexDirection: 'row' }}>
<TabItem label="Open drawer" onPress={onOpenDrawerPress} />
{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 onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
return (
<TabItem label={label} onPress={onPress} isFocused={isFocused} />
);
})}
</View>
);
};
const TabOneScreen = () => (
<SafeAreaView>
<Text>Tab One screen</Text>
</SafeAreaView>
);
const TabTwoScreen = () => (
<SafeAreaView>
<Text>Tab Two screen</Text>
</SafeAreaView>
);
const TabNavigator = () => {
return (
<Tab.Navigator tabBar={MyTabBar}>
<Tab.Screen name="TabOneScreen" component={TabOneScreen} />
<Tab.Screen name="TabTwoScreen" component={TabTwoScreen} />
</Tab.Navigator>
);
};
const Drawer = createDrawerNavigator();
const Notifications = ({ navigation }) => (
<SafeAreaView>
<Text>Notifications Screen</Text>
<Button title="Open drawer" onPress={navigation.openDrawer} />
</SafeAreaView>
);
const Router = () => {
return (
<Drawer.Navigator>
<Drawer.Screen name="Home" component={TabNavigator} />
<Drawer.Screen name="Notifications" component={Notifications} />
</Drawer.Navigator>
);
};
export default Router;
Maybe the only "drawback" would be that you have to customize the custom tab bar so that you can achieve your desired look and feel. However, that could also prove to be better in the long run, as you have more control over your tab bar and its items.
I am using React Navigation V5, I wanna custom drawer Navigation content which contents the image on top and some others navigation items unders
Here is my drawer items:
Image (custom view)
Profile
Products
Orders
Here is my code my custom drawer content.
export const CustomDrawerContent = props => {
return (
<SafeAreaView style={styles.customDrawer}>
<View
style={{ flex: 1 }}
>
<DrawerContentScrollView {...props}>
<TouchableNativeFeedback onPress={() => { console.log('go profile'); }}>
<View style={styles.userContainer}>
<View style={styles.imageContainer}>
<Image
style={styles.image}
source={{ uri: 'https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTLCta_MQcJFd2kpz8HwXFm-6vxVqXzRUgCOIuhs94Q32GG8EeJ' }}
/>
</View>
<Text style={styles.name}>Nguyen van Admin</Text>
</View>
</TouchableNativeFeedback>
<DrawerItemList {...props} />
</DrawerContentScrollView>
<DrawerItem
label="Đăng xuất"
style={{
borderWidth: 1,
}}
labelStyle={{
color: 'black'
}}
icon={({ focused, color, size }) => <Ionicons
size={23}
color={color}
name={Platform.OS === 'android' ? 'md-exit' : 'ios-exit-outline'}
/>}
/>
</View>
</SafeAreaView>
);
}
So If the profile screen existed in drawer, By clicking to the image i can use
props.navigate("profile")
But if I remove the profile screen from the drawer screens. I can not navigate to profile anymore.
How do i archive navigate to profile screen without adding it the drawer screens?
Or Can I hide profile item from drawer items?
To hide a menu item from drawer, use Array.map(...) instead of <DrawerItemList {...props} /> in custom drawer content.,
{drawerItems.map((item, index) => {
return (
<DrawerItem
label={item.drawerLabel}
onPress={() => props.navigation.navigate(item.routeName)}
/>
);
})}
and add a useEffect hook to custom drawer content like below,
let [drawerItems, setDrawerItems] = useState([]);
useEffect(() => {
let drawerItemsList = [];
for (const key in props.descriptors) {
if (props.descriptors.hasOwnProperty(key)) {
if (!key.includes('profile')) {
const element = props.descriptors[key];
element.options.routeName = key.substring(0, key.indexOf('-'));
drawerItemsList.push(element.options);
}
}
}
setDrawerItems(drawerItemsList);
}, []);
Another approach.,
Create an Array like below in the custom drawer content.,
const drawerItemsList = [
{
drawerLabel: 'Products',
drawerIcon: 'product',
routeName: 'products',
active: true,
},
{
drawerLabel: 'Orders',
drawerIcon: 'order',
routeName: 'orders',
active: false,
},
];
let [drawerItems, setDrawerItems] = useState(drawerItemsList);
and instead of <DrawerItemList {...props} /> use <Flatlist /> like below.,
<View>
<FlatList
data={drawerItems}
keyExtractor={(item)=>item.routeName.trim()}
renderItem={({item,index})=>(
<DrawerItem
label={item.drawerLabel}
icon={({color, size}) => <Ionicons name={item.drawerIcon} color={item.active?'#1e90ff':'#ccc'} size={size} />}
labelStyle={[item.active?{color: '#1e90ff'}:{color: '#ccc'}]}
style={item.active?{backgroundColor: '#1e90ff20'}:null}
onPress={() => {
drawerItemsList.forEach((element,i) => {
i!==index?element.active=false:element.active=true
});
setDrawerItems(drawerItemsList)
props.navigation.navigate(item.routeName)
}}
/>
)}
/>
</View>
I have been stuck in this issue since yesterday and I cannot find a solution.
I have been trying to adjust the color of safeArea in iPhone X, it's working well on the top, and in the bottom as well for screens with no tab, however, in screens with tab navigator, the bottom safeArea is always white as shown in the screenshot. Does anyone know how to solve this issue?
Also, I want to ask if it would be better to use normal SafeAreaView component and remove the SafeAreaProvider and remove react-native-safe-area-context package, I just added it as a trial to solve this problem but I was first working with the react native normal SafeAreaView component;
In App component:
import { SafeAreaProvider } from "react-native-safe-area-context";
<SafeAreaProvider>
<NavigationContainer>
<CatNavigator />
</NavigationContainer>
</SafeAreaProvider>
In the CatNavigator component:
const CatNavigator = () => {
return (
<Drawer.Navigator
initialRouteName="Home" >
<Drawer.Screen
name="Home"
component={SettingsNavigator}
options={{ title: "Inicio" }}
/>
</Drawer.Navigator>
In the settings tab navigator:
const SettingsNavigator = () => {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
const iconType = Platform.OS === "ios" ? "ios" : "md";
if (route.name === "Home") {
iconName = iconType + "-home";
} else if (route.name === "Settings") {
iconName = iconType + "-settings";
}
const tabColor = focused ? Colors.buttonBackColor : Colors.titleColor;
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
activeTintColor: Colors.activeTabColor,
inactiveTintColor: Colors.inactiveTabColor,
activeBackgroundColor: Colors.tabBackgroundColor,
inactiveBackgroundColor: Colors.tabBackgroundColor,
safeAreaInset: { bottom: "never", top: "never" },
}}
>
<Tab.Screen
name="Home"
component={HomeNavigator}
options={{ title: "Inicio" }}
/>
<Tab.Screen
name="Settings"
component={SettingStackNavigator}
options={{ title: "Ajustes" }}
/>
</Tab.Navigator>
In SettingsNavigator:
const SettingStackNavigator = (props) => {
return (
<SettingStack.Navigator screenOptions={defaultNavOptions}>
<SettingStack.Screen
name="Settings"
component={SettingScreen}
options={settingsOptions}
/>
</SettingStack.Navigator>
);
};
And finally in SettingScreen:
import { SafeAreaView } from "react-native-safe-area-context";
return (
<SafeAreaView
style={{
flex: 1,
backgroundColor: Colors.backgroundColor,
justifyContent: "center",
alignItems: "center",
}}
>
{colorScheme === "dark" && <StatusBar barStyle="light-content" />}
// Other components
</SafeAreaView>
If you want to change that little guy at the bottoms color you need add the style option to your Tab.Navigator, like so
tabBarOptions={{
style: {
backgroundColor: Colors.tabBackgroundColor,
},
}}
I'm migrating a RN project version 4 to 5.
I was using the tabBarComponent option to replace tab bar component with a custom one. Old docs.
How to achieve the same in version 5, I can't find this option in the new docs.
Here is the new API way to achieve a custom tab component:
import { View, Text, TouchableOpacity } from 'react-native';
function MyTabBar({ state, descriptors, navigation }) {
return (
<View style={{ flexDirection: 'row' }}>
{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 onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1 }}
>
<Text style={{ color: isFocused ? '#673ab7' : '#222' }}>
{label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
// ...
<Tab.Navigator tabBar={props => <MyTabBar {...props} />}>
{...}
</Tab.Navigator>
This link will surely help.
https://reactnavigation.org/docs/en/next/bottom-tab-navigator.html#tabbar
Hope this helps. Cheers!