How to add clients logo on bottom Tab Bar - react-native

i am creating an android based mobile app using react native. I want to add a logo on my bottom tab. The tab bar also contains 4 tabs. I need to display a logo on left corner of my device. Logo is not a clickable one, just an image.

For React Navigation 5.
You can show custom BottomTabBar with tabBar options.
Define Tab stack
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator tabBar={props => <BottomTabBar {...props} />}>
<Tab.Screen name="Tab1" component={Screen3} />
<Tab.Screen name="Tab2" component={Screen2} />
<Tab.Screen name="Tab3" component={Screen2} />
<Tab.Screen name="Tab4" component={Screen2} />
</Tab.Navigator>
);
}
Create Custom BottomTabBar
import React from 'react';
import {View,Text,Image,Platform,TouchableOpacity,StyleSheet} from 'react-native';
const BottomTabBar = ({state, navigation, ...props}) => {
const {routes = [], index: activeIndex} = state;
return (
<View style={styles.container}>
<Image
source={require('../../assets/logo.png')}
style={styles.imageIcon}
/>
<View style={styles.tabContainer}>
{routes.map((it, index) => {
return (
<TouchableOpacity
onPress={() => {
navigation.jumpTo(it.name);
}}
style={[
styles.tabButton,
{
borderBottomWidth: activeIndex === index ? 1 : 0,
},
]}>
<Text>{it.name}</Text>
</TouchableOpacity>
);
})}
</View>
</View>
);
};
export default BottomTabBar;
const styles = StyleSheet.create({
tabButton: {
flex: 1,
height: 50,
justifyContent: 'center',
alignItems: 'center',
borderBottomColor: 'red',
},
tabContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-evenly',
},
imageIcon: {
width: 50,
height: 50,
resizeMode: 'contain',
},
container: {
width: '100%',
flexDirection: 'row',
alignItems: 'center',
height: 64,
paddingHorizontal: 16,
backgroundColor: 'white',
paddingBottom: Platform.OS === 'ios' ? 15 : 0,
},
});
ScreenShot

Related

React Native How to show an element above bottom tab navigator

I am trying to show a media player above the bottom tab navigator just like in spotify app.
It should stay there in same state across all the screens (bottom tabs).
Any suggestions or approaches to implement this in React native ?
You just need to calculate the height of Bottom Navbar and make a View with Position:'absolute' and to keep it above Bottom Navbar assign the height of Navbar to 'bottom' of this View.
<View>
<View
style={{
position: "absolute",
backgroundColor: "#4470AE",
bottom: 75,
flexDirection: "row",
width: "90%",
height: 60,
paddingVertical: "3%",
alignSelf: "center",
borderRadius: 12,
margin: "3%",
}}
></View>
<View
style={{
position: "absolute",
backgroundColor: "orange",
bottom: 0,
height: 75,
flexDirection: "row",
width: "100%",
justifyContent: "space-around",
paddingVertical: "3%",
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
}}
>
</View>
</View>
I would suggest to use an React.Context as you wanna show the same object (music control panel) on each screen with same state and not an replica on each screen.
For positioning I suggest using an absolute position. This works together with "react-navigation" because header and bottom areas are changed.
This means bottom:0 is no longer the screens bottom within a tab navigator it's right above the TabBar.
Here is an example:
import * as React from 'react';
import { Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { MusicControlPanelContext, MusicControlPanelConsumer } from './components/MusicControlContext';
import {MusicControlPanel} from './components/MusicContorlPanel'
function HomeScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
<MusicControlPanelConsumer/>
</View>
);
}
function SettingsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
<MusicControlPanelConsumer/>
</View>
);
}
const Tab = createBottomTabNavigator();
export default function App() {
return (
<MusicControlPanelContext.Provider value={MusicControlPanel}>
<>
<NavigationContainer>
<Tab.Navigator
tabBarOptions={{
activeTintColor: '#000000',
inactiveTintColor: 'gray',
activeBackgroundColor: '#ff0000',
inactiveBackgroundColor: '#ff0000',
style: {
backgroundColor: '#ffffff',
},
}}>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
</>
</MusicControlPanelContext.Provider>
);
}
import * as React from 'react';
import { MusicControlPanelContext } from './MusicControlPanelContext';
import { View } from 'react-native';
function MusicControlPanelConsumer() {
return (
<MusicControlPanelContext.Consumer>
{(Component) => <Component/>}
</MusicControlPanelContext.Consumer>
);
}
export { MusicControlPanelConsumer };
import * as React from 'react';
import {MusicControlPanel} from '../MusicContorlPanel'
export const MusicControlPanelContext = React.createContext<React.FC>(MusicControlPanel);
import * as React from 'react';
import { View, Text, Pressable } from 'react-native';
export const MusicControlPanel: React.FC = () => {
const [startStop, setStartStop] = React.useState(false);
return (
<View
style={{
position: 'absolute',
width: '90%',
height: 100,
backgroundColor: '#ff00ff',
bottom: 10,
justifyContent: 'center',
alignItems: 'center'
}}>
<Pressable
onPress={() =>
setStartStop((val) => {
return !val;
})
}>
<Text>{startStop ? 'Start' : 'Stop'}</Text>
</Pressable>
</View>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I just solved this problem
import BottomTabBar
call tabBar props in tab navigator
wrap it with empty tag
import and insert your component above the BottomTabBar
import { BottomTabBar} from '#react-navigation/bottom-tabs';
import SmallVideoPlay from 'YOUR_PATH_OF_THE_COMPONENT';
<Tab.Navigator
tabBar={(props) => (
<>
< SmallVideoPlay />
<BottomTabBar {...props} />
</>
)}
>
<Tab.Screen name="HomeStack" component={Home}/>
....
</Tab.Navigator/>

Blank screen when I put Drawer Navigator as the main navigator in index.tsx

If I put Drawer Navigator in index.tsx I only get a blank screen.
Tried wrapping NavigationContainer in index.tsx in a View with flex: 1 but it didn't work.
Also I don't have alignItems: 'center' in App.tsx to remove as this answer suggests. (https://stackoverflow.com/a/62647417/13117660)
index.tsx:
const Drawer = createDrawerNavigator();
export const store = configureStore({});
export default () => {
return (
<Provider store={store}>
<DatabaseConnectionProvider>
<NavigationContainer>
<Drawer.Navigator drawerContent={(props: any) => <CustomDrawerContent {...props} />} screenOptions={{
headerTitle: () => <Image source={require('../assets/logo_navbar.jpg')} style={{
width: 120,
height: 40,
alignContent: 'center'
}} />,
headerTitleAlign: 'center',
headerStyle: {
backgroundColor: Colors.RED,
}
}} >
<Drawer.Screen name='Home' component={HomeScreen}></Drawer.Screen>
</Drawer.Navigator>
</NavigationContainer>
</DatabaseConnectionProvider>
</Provider>
);
};
CustomDrawerContent.tsx:
const CustomDrawerContent: FC = (props: any) => {
return (
<View style={{ flex: 1, backgroundColor: Colors.RED }}>
<DrawerContentScrollView {...props} style={styles.drawerContent2}>
<View style={styles.drawerContent}>
<Drawer.Section style={styles.drawerSection}>
<DrawerItem
labelStyle={{ color: Colors.WHITE, fontSize: 20, marginLeft: 20 }}
label={"Home"}
onPress={() => {
props.navigation.navigate("Home");
}}
/>
<DrawerItem
labelStyle={{ color: Colors.WHITE, fontSize: 20, marginLeft: 20 }}
label={"About us"}
onPress={() => {
props.navigation.navigate("AboutUs");
}}
/>
<DrawerItem
labelStyle={{ color: Colors.WHITE, fontSize: 20, marginLeft: 20 }}
label={"Impresum"}
onPress={() => {
props.navigation.navigate("Impresum");
}}
/>
<DrawerItem
labelStyle={{ color: Colors.WHITE, fontSize: 20, marginLeft: 20 }}
label={"Contact"}
onPress={() => {
props.navigation.navigate("Contact");
}}
/>
</Drawer.Section>
</View>
</DrawerContentScrollView>
</View>
);
};
export default CustomDrawerContent;
const styles = StyleSheet.create({
drawerContent: {
flex: 1,
backgroundColor: Colors.RED,
},
drawerContent2: {
flex: 1,
},
labels: {
fontSize: 30,
},
userInfoSection: {
paddingLeft: 20,
},
title: {
fontSize: 16,
marginTop: 3,
fontWeight: "bold",
},
caption: {
fontSize: 14,
lineHeight: 14,
},
row: {
marginTop: 20,
flexDirection: "row",
alignItems: "center",
},
section: {
flexDirection: "row",
alignItems: "center",
marginRight: 15,
},
paragraph: {
fontWeight: "bold",
marginRight: 3,
},
drawerSection: {
marginTop: 15,
fontWeight: "bold",
backgroundColor: Colors.RED,
},
});
Edit:
Also when I use the Drawer Navigator like this in index.tsx it also shows just blank screen
return (
<Provider store={store}>
<DatabaseConnectionProvider>
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name='Home' component={HomeScreen}></Drawer.Screen>
<Drawer.Screen name='About' component={AboutUsScreen}></Drawer.Screen>
</Drawer.Navigator>
</NavigationContainer>
</DatabaseConnectionProvider>
</Provider>
);
When I put a breakpoint on the return of HomeScreen component it gets hit only the first time the app loads, but nothing renders on screen. If I reload the app than the HomeScreen breakpoint is not hit. I have to stop the server and run the app again every time. But the blank screen is always there.
If I change the Drawer Navigatior to Stack Navigatior than everything works. But I need the Drawer Navigator to be the main navigator.
I am using expo, #react-navigation/drawer 6.3.0 and #react-navigation/native 6.0.8
So I found out what wasn't working. My debbuger was on and it was causing problems as explained here: https://github.com/react-navigation/react-navigation/issues/10253
I am debugging in VS code, not in Chrome Debugger but still when I turn off debugging it works. If the debugger is on you have to put
useLegacyImplementation={false}
as a prop in Drawer.Navigator. It is little laggy but works.
I am using react-native-reanimated 2.4.0

i have a problem with passing params to a route in react native navigation. `route.params` is undefined sorry am abit new to react native

Here is my homepage. and i was to navigate to the bookings page while passing a uuid as a param to the bookings page.
import React from "react";
import {
Text,
View,
StatusBar,
ActivityIndicator,
StyleSheet,
SafeAreaView,
TextInput,
TouchableOpacity,
} from "react-native";
import { useFocusEffect } from "#react-navigation/native";
import APIKit from "../config/api/APIKit";
import Icon from "react-native-vector-icons/Feather";
import Card from "../components/Card";
import { ScrollView } from "react-native-gesture-handler";
function HomeScreen({ navigation }) {
const [loading, setLoading] = React.useState(true);
const [items, setItems] = React.useState([]);
const [limit, setLimit] = React.useState(7);
const image = require("../assets/pic1.jpg");
useFocusEffect(
React.useCallback(() => {
getData();
return () => {
setItems([]);
setLimit(9);
};
}, [])
);
const getData = () => {
setLoading(true);
APIKit.get(`items/?limit=${limit}&offset=1`)
.then((res) => {
console.log(res.data);
setLimit(limit + 6);
setItems([items, ...res.data.results]);
setLoading(false);
})
.catch((e) => console.log(e.request.response));
};
return (
<SafeAreaView
style={{ alignItems: "flex-start", justifyContent: "center" }}
>
<StatusBar style="auto" />
<View style={{ padding: 8 }}>
<View
style={{
flexDirection: "row",
alignItems: "center",
width: "100%",
marginTop: 5,
}}
>
<TextInput
placeholder={"Enter Item Name"}
style={{
flex: 1,
backgroundColor: "white",
height: 40,
borderColor: "black",
borderWidth: 1,
borderRadius: 5,
margin: 2,
paddingLeft: 8,
}}
/>
<TouchableOpacity
style={{
alignItems: "center",
alignSelf: "center",
borderWidth: 1,
width: 50,
borderRadius: 500,
}}
>
<Icon name="search" size={25} />
</TouchableOpacity>
</View>
<View style={{ flex: 1, marginTop: 20 }}>
<ScrollView>
<View
style={{
flex: 1,
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "flex-start",
}}
>
{items !== []
? items.map((item, key) => {
return (
<Card
key={key}
name={item.item_name}
description={item.description}
onHandlePress={() =>
navigation.navigate("payment-options", {
uuid: item.uuid,
price: item.cost,
})
}
image={image}
/>
);
})
: null}
</View>
<TouchableOpacity
activeOpacity={0.9}
onPress={getData}
//On Click of button load more data
style={styles.loadMoreBtn}
>
<Text style={styles.btnText}>Load More</Text>
{loading ? (
<ActivityIndicator color="white" style={{ marginLeft: 8 }} />
) : null}
</TouchableOpacity>
</ScrollView>
</View>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
footer: {
padding: 10,
justifyContent: "center",
alignItems: "center",
flexDirection: "row",
},
loadMoreBtn: {
padding: 10,
width: 350,
marginHorizontal: 20,
alignSelf: "center",
backgroundColor: "#01ab9d",
borderRadius: 4,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
},
btnText: {
color: "white",
fontSize: 15,
textAlign: "center",
},
});
export default HomeScreen;
and the booking page looks like this
import { Text, View, StatusBar, SafeAreaView, StyleSheet } from "react-native";
import { Button } from "react-native-elements";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
function PaymentOptionScreen(props) {
return (
<SafeAreaView
style={{ flex: 1, alignItems: "center", justifyContent: "center" }}
>
<StatusBar style="auto" />
<View style={{ flex: 1, justifyContent: "flex-start", margin: 20 }}>
<Button
title="Pay Now"
onPress={() =>
console.log(props.route.params)
}
containerStyle={styles.ButtonContainer}
buttonStyle={{ height: 60, justifyContent: "flex-start" }}
icon={<Icon name="cash" size={40} color="white" />}
/>
<Button
title="Pay on delivery"
containerStyle={styles.ButtonContainer}
buttonStyle={{ height: 60, justifyContent: "flex-start" }}
icon={<Icon name="cash" size={40} color="white" />}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
ButtonContainer: {
width: 380,
},
});
export default PaymentOptionScreen;
here is a snippet of the of the app.js it may help
<AuthContext.Provider value={authContext}>
<NavigationContainer>
<Drawer.Navigator
initialRouteName="Home"
drawerContent={(props) => <DrawerContent {...props} />}
>
<Drawer.Screen name="Home" component={HomeStackScreen}/>
<Drawer.Screen name="Profile" component={ProfileStackScreen}/>
<Drawer.Screen name="Bookings" component={BookingsStackScreen}/>
<Drawer.Screen name="payment-options" component={PaymentOptionsScreenStack}/>
<Drawer.Screen name="Add Item" component={AddItemStackScreen}/>
<Drawer.Screen name="Settings" component={SettingsStackScreen}/>
<Drawer.Screen name="Messages" component={MessagesStackScreen}/>
</Drawer.Navigator>
</NavigationContainer>
</AuthContext.Provider>
Can you print the props? I think you have to pass it as object {props}
If you are using navigation 5.0 . Try this
const App=({navigation, route })=>{
const uuid = route.param;
return (
<View</View>
);
}

tab bar icons not perfectly centered when screen changes

I have a issue with my costume tab bar and that is when I select other screens icons lose their position a little bit and shift to for example right. for better understanding consider these two images:
you can see this compass icon is not centered perfectly:
but when I tap on screen this become perfectly centered:
here's my styles on custom bottom tab bar :
const TabComponent = ({label, accessibilityState, onPress}) => {
const focused = accessibilityState.selected;
const icon = focused ? ICONS[`${label}Active`] : ICONS[label]
return (
<TouchableWithoutFeedback onPress={onPress}>
<View focused={focused} style={[styles.container, focused ? styles.focused : null]}>
<Image style={styles.icon} source={icon} />
</View>
</TouchableWithoutFeedback>
)
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginHorizontal: 20
},
focused: {
backgroundColor: '#3170FF',
borderRadius: 25,
width: 46,
height: 75,
position: 'relative',
bottom: 20
},
icon: {
width: 24,
height: 24,
}
})
and below is my styles on bottom tab itself:
<Tab.Navigator tabBarOptions={{
style: {
borderTopLeftRadius: 23,
borderTopRightRadius: 23,
height: 64,
alignItems: 'center',
}
}} >
<Tab.Screen name="Home" component={Home} options={{
tabBarButton: (props) => <TabComponent label='home' {...props} />,
}}/>
...
how can I fix this issue? any help would be great.

Adding a custom 'Add' button to createMaterialBottomTabNavigator in react navigation 5

I'm making a project (react native, expo, react navigation 5) where I wanted to add a custom 'add' button to the bottom tabs, but since...
A navigator can only contain 'Screen' components as its direct
children
...I needed to find a way to pass my custom component.
Seemed easy enough, I mean there are docs:
https://reactnavigation.org/docs/material-bottom-tab-navigator
https://reactnavigation.org/docs/bottom-tab-navigator
https://reactnavigation.org/docs/custom-navigators/
...but in looking at these and the questions from others I either only found muy complicado examples or examples of how to achieve this in earlier versions.
In the end I found a simple solution that so far works like a charm (fully grateful to any suggestions as to why this might be a terrible idea).
Figured I post my solution if anyone is in a similar pickle. See answer below.
Place the component outside the navigator, and position it above the tabs with css. Adjust the icons of the tabs to the left and right as seen in the example.
Like I said above, suggestions on how to achieve this in a different way warmly welcome, but haven't encountered any issues yet (knock on wood).
Here's what it looks like:
And here's the bunny:
import React from 'react';
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs';
import { Ionicons, MaterialIcons, FontAwesome } from '#expo/vector-icons';
import AddButton from '../../components/UI/AddButton';
import SpotlightProductsScreen from './SpotlightProductsScreen';
import ProductsScreen from './ProductsScreen';
import UserSpotlightScreen from './../user/UserSpotlightScreen';
import UserProductsScreen from './../user/UserProductsScreen';
const ProductsOverviewScreen = props => {
const Tab = createMaterialBottomTabNavigator();
return (
<>
<AddButton
navigation={props.navigation}
style={{
position: 'absolute',
zIndex: 99,
bottom: 5,
alignSelf: 'center',
shadowColor: 'black',
shadowOpacity: 0.15,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 8,
elevation: 3 //Because shadow only work on iOS, elevation is same thing but for android.
}}
/>
<Tab.Navigator
initialRouteName="Spotlight"
labeled={false}
shifting={true}
activeColor="#f0edf6"
inactiveColor="#3e2465"
barStyle={{ backgroundColor: 'rgba(127,63,191,.9)' }}
>
<Tab.Screen
name="Spotlight"
component={SpotlightProductsScreen}
options={{
tabBarIcon: ({ color }) => (
<Ionicons
name={
Platform.OS === 'android'
? 'md-notifications'
: 'ios-notifications'
}
color={color}
size={27}
style={{
marginLeft: -35
}}
/>
)
}}
/>
<Tab.Screen
name="Förråd"
component={ProductsScreen}
options={{
tabBarIcon: ({ color }) => (
<MaterialIcons
name={'file-download'}
color={color}
size={27}
style={{
marginLeft: -70
}}
/>
)
}}
/>
<Tab.Screen
name="Mitt Förråd"
component={UserProductsScreen}
options={{
tabBarIcon: ({ color }) => (
<MaterialIcons
name={'file-upload'}
color={color}
size={30}
style={{
marginRight: -70
}}
/>
)
}}
/>
<Tab.Screen
name="Min Sida"
component={UserSpotlightScreen}
options={{
tabBarBadge: 4,
tabBarIcon: ({ color }) => (
<FontAwesome
name={'user'}
color={color}
size={30}
style={{
marginRight: -35
}}
/>
)
}}
/>
</Tab.Navigator>
</>
);
};
export default ProductsOverviewScreen;
You can try this:
<Tab.Screen
name = "Button"
component={ScanStack}
options={{
tabBarButton:()=>
<View style={{position:'relative',bottom:35,alignItems:'center', justifyContent:'space-around',height:85}}>
<Icon
name="barcode-scan"
type = "material-community"
reverse
color={'yellow'}
reverseColor='black'
containerStyle={{padding:0,margin:0,elevation:5}}
onPress={()=>console.log('Hi')}
size={30}/>
<Text>Scan</Text>
</View>
}}/>
In the component it is necessary to use a valid react-component, I tried to use component={()=>null} but a warn appeared in the console
Result
I had the same problem, I needed to add a custom component, not related with a screen, to the tab navigator and everything I tried it failed. In my case I was trying with createMaterialTopTabNavigator.
Documentation of React Navigation 5 is a little rough and there isn't a lot of examples of React Navigation 5, but after many attempts I could create a custom component and style it myself so I can mix the tabs created by the routes and the custom buttons embedded in the tab navigator.
import * as React from 'react';
import { View } from 'react-native'
import {
NavigationHelpersContext,
useNavigationBuilder,
TabRouter,
TabActions,
createNavigatorFactory,
} from '#react-navigation/native';
import styled from 'styled-components'
import Colors from '../constants/Colors';
const customTabNavigator = ({
initialRouteName,
children,
screenOptions,
tabContainerStyle,
contentStyle,
leftIcon,
rightIcon
}) => {
const { state, navigation, descriptors } = useNavigationBuilder(TabRouter, {
children,
screenOptions,
initialRouteName,
});
return (
<NavigationHelpersContext.Provider value={navigation}>
<OuterWrapper style={tabContainerStyle}>
{ leftIcon }
<TabWrapper>
{state.routes.map((route, i) => {
return (
<Tab
key={route.key}
onPress={() => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!event.defaultPrevented) {
navigation.dispatch({
...TabActions.jumpTo(route.name),
target: state.key,
});
}
}}
style={descriptors[route.key].options.tabStyle}
>
{ descriptors[route.key].options.label ?? <Label active={state.index === i}>{descriptors[route.key].options.title || route.name}</Label> }
</Tab>
)
})}
</TabWrapper>
{ rightIcon }
</OuterWrapper>
<View style={[{ flex: 1 }, contentStyle]}>
{descriptors[state.routes[state.index].key].render()}
</View>
</NavigationHelpersContext.Provider>
);
}
const OuterWrapper = styled.View`
height: 55px;
flex-direction: row;
justify-content: space-between;
background-color: ${Colors.grey1};
`
const TabWrapper = styled.View`
flex: 1;
flex-direction: row;
justify-content: space-evenly;
`
const Tab = styled.TouchableOpacity`
padding: 0 24px;
justify-content: center;
height: 100%;
`
const Label = styled.Text`
font-family: Futura-Medium;
font-size: 26px;
color: ${({ active }) => active ? Colors.grey6 : Colors.grey3};
`
export default createNavigatorFactory(customTabNavigator)
import customTabNavigator from './customTabNavigator'
import * as React from 'react';
import { View, Image } from 'react-native'
import {
ProjectsScreen,
RenderScreen,
EventsScreen,
CameraScreen
} from '../screens';
import Colors from '../constants/Colors'
import logo from '../assets/images/icon.png'
import { Ionicons } from '#expo/vector-icons';
import { TouchableOpacity } from 'react-native-gesture-handler';
const TopTab = customTabNavigator();
const INITIAL_ROUTE_NAME = 'Home';
export default function MainNavigator({ navigation, route }) {
navigation.setOptions({ headerTitle: getHeaderTitle(route) });
return (
<TopTab.Navigator
initialRouteName={INITIAL_ROUTE_NAME}
leftIcon={(
<TouchableOpacity style={{ height: "100%", justifyContent: "center" }} onPress={() => alert("Whatever")}>
<Image source={logo} style={{ resizeMode: "center", width: 70, height: 40 }} />
</TouchableOpacity>
)}
>
<TopTab.Screen
name="Home"
component={ProjectsScreen}
options={{
title: 'Proyectos',
}}
/>
<TopTab.Screen
name="Preview"
component={EventsScreen}
options={{
title: 'Eventos',
}}
/>
<TopTab.Screen
name="Render"
component={RenderScreen}
options={{
title: 'Mi cuenta',
}}
/>
<TopTab.Screen
name="Camera"
component={CameraScreen}
options={{
title: "Camera",
label: (
<View style={{ width: 36, height: 32, backgroundColor: Colors.grey3, borderRadius: 3, alignItems: "center", justifyContent: "center" }}>
<Ionicons name="md-camera" style={{ color: Colors.grey5 }} size={25} />
</View>
),
tabStyle: { flexDirection: "row", alignItems: "center", justifyContent: "flex-end", flex: 1 }
}}
/>
</TopTab.Navigator>
);
}
function getHeaderTitle(route) {
const routeName = route.state?.routes[route.state.index]?.name ?? INITIAL_ROUTE_NAME;
switch (routeName) {
case 'Home':
return 'Montar vídeo';
case 'Preview':
return 'Previsualizar vídeo';
case 'Render':
return 'Renderizar';
case 'Gallery':
return 'Galería'
case 'Camera':
return 'Camera'
}
}
To the example of https://reactnavigation.org/docs/custom-navigators I added a different styling and two new props, leftIcon and rightIcon. This props receive a component for render it to the corresponding side of the tab wrapper. And this components can be TouchableWhatevers with a custom onPress not related with a screen :P
I hope it helps, I almost throw myself through the window until I made it work, haha
step-1:- In bottomTabNav.js to component-prop give a screen which returns nothing
import React from 'react'
const AddMoreScreen = () => {
return null
}
export default AddMoreScreen
//I created AddMoreScreen.js component
step-2:- As mentioned in step-1, I am giving a screen that renders nothing to component prop, later in options prop I will tap tabBarButton object & returns a custom button to it
...
<Tab.Screen
name={"Add"}
component={AddMoreScreen}
options={{
tabBarButton: ()=> <AddMoreModal />
}}
/>
...
step-3:- and finally our center button code, in my case, if I press button a modal has to appear from the bottom, just change the below codes for your requirements. AddMoreModal.js
export default function AddMoreModal() {
const [modalVisible, setModalVisible] = useState(false);
return (
<View style={{ marginTop: 15, marginLeft: 10, marginRight: 10, }}>
<TouchableOpacity
onPress={() => {
setModalVisible(true);
}}
>
<View style={styles.buttonStyle}>
<Image
source={icons.quickAddOutlined}
style={styles.bottomTabImage}
/>
<Text style={styles.bottomTabText}>Add</Text>
</View>
</TouchableOpacity>
<View style={styles.container}>
<Modal
backdropOpacity={0.3}
isVisible={modalVisible}
onBackdropPress={() => setModalVisible(false)}
style={styles.contentView}
>
<View style={styles.content}>
<Text style={styles.contentTitle}>Hi 👋!</Text>
<Text>Welcome to CRAZY MIDDLE BUTTON! forgotten by react-native navigation</Text>
</View>
</Modal>
</View>
</View>
);
}
const styles = StyleSheet.create({
content: {
backgroundColor: "white",
padding: 22,
justifyContent: "center",
alignItems: "center",
borderTopRightRadius: 17,
borderTopLeftRadius: 17,
},
contentTitle: {
fontSize: 20,
marginBottom: 12,
},
contentView: {
justifyContent: "flex-end",
margin: 0,
},
buttonStyle: {
marginBottom: 0,
alignItems:'center',
justifyContent:'center'
},
bottomTabImage: {
width: 25,
height: 25,
},
bottomTabText: {
marginTop: 4,
fontSize: FONTS.body3.fontSize,
fontFamily: FONTS.body3.fontFamily,
color: COLORS.fontGray90,
},
});
step-4:-