Global button for bottom tab bar in react-navigation V5

nitin varda
3 min readAug 26, 2021

Hey Guys, I’m going to share one of the solutions for making a tab navigator screen button as a global button that can have its own functionality on different screens.

Github Link : https://github.com/nitinvarda/Global-tabBar-button

To start with let’s create a new project

npx react-native init demoProject

After setting up the project, install react-navigation v5 and its dependencies

npm install react-native-screens react-native-safe-area-context @react-navigation/native @react-navigation/bottom-tabs

After installing all dependencies we need to create a Context in App level.

AppContext.js

import React from 'react'
const AppContext = React.createContext()
export default AppContext

Now we are going to configure App.js

App.js

import React,{useState,useRef} from 'react';
import {NavigationContainer} from '@react-navigation/native'
import TabStack from './screens/TabStack';
import AppContext from './config/AppContext';
const App = () => {
const [openGlobalModalFor,setOpenGlobalModalFor] = useState(null)
const navigationRef = useRef()

const closeModal = ()=>{
setOpenGlobalModalFor(null)
}
return (
<AppContext.Provider value={{openGlobalModalFor,closeModal}}>
<NavigationContainer ref={navigationRef}
onStateChange
={()=>{
setOpenGlobalModalFor(null)
}}
>
<Tab.Navigator
initialRouteName="screen1"
tabBarOptions={{showLabel:false}}
>
<Tab.Screen name="screen1"
component={Screen1}
options={{
tabBarIcon:()=><Text>1</Text>,
headerShown:false
}}
/>
<Tab.Screen name="buttonScreen"
component={ButtonScreen}
options={{
tabBarIcon:(params)=>{
return <View
style={{
width:50,
height:50,
backgroundColor:'orange',
borderRadius:50,
flexDirection:'row',
justifyContent:'center',
alignItems:'center'
}}>
<Text style={{
fontSize:35,
transform:[{rotate:openGlobalModalFor?
"45deg" : "0deg"}]}}>
+</Text></View>
},
showLabel:false
}}
listeners={(params)=>({
tabPress:(event)=>{
event.preventDefault()
const currentScreenName =
navigationRef.current.getCurrentRoute().name
console.log(currentScreenName)
if(openGlobalModalFor){
setOpenGlobalModalFor(null)
}
else{
if(currentScreenName == 'screen1'){
setOpenGlobalModalFor('screen1')
}
else if(currentScreenName == 'screen2'){
setOpenGlobalModalFor('screen2')
}
}
}
})}
/>
<Tab.Screen name="screen2"
component={Screen2}
options={{
tabBarIcon:()=><Text>2</Text>,
headerShown:false
}}
/>
</NavigationContainer>
</AppContext.Provider>
)
}
export default App;

We need to create a reference to navigation container so that we can check in which screen we are with “navigationRef.current.getCurrentRoute().name”.

Create a new variable “openGlobalModalFor” which can be stored the current screen name and pass it to other screens with context api. initially it will be null.

We need to return null for the button screen component so that it wont show white screen, later we need to add as listener to button Screen . so that when ever we press, an event is passed.

When the button is pressed we check wether the “openGlobalModalFor” is null or not , if it is not null then it has a screen name in it so we make it null.

if it is null then it doesn’t have any screen name yet, so we check the current screen name and set that value to “openGlobalModalFor”, this value is being sent through context api to all screens.

Screen1.js

import React,{useContext} from 'react'
import { View, Text, TouchableOpacity } from 'react-native'
import AppContext from '../config/AppContext'
export default function Screen1() {
const AppState = useContext(AppContext)

return (
<View
style={{
position:'relative',
width:'100%',
height:'100%',
backgroundColor:AppState.openGlobalModalFor=='screen1'?
'rgba(0,0,0,0.4)' : 'transparent'}}>
<Text style={{fontSize:40,textAlign:'center'}}>Screen1</Text>
{AppState.openGlobalModalFor=='screen1' && (
<View
style={{
width:'100%',
height:'100%',
flexDirection:'row',
justifyContent:'center',
alignItems:'center'
}}>
<View
style={{
position:'absolute',
backgroundColor:'white',
width:'70%',
height:'40%',
borderRadius:15,
padding:20
}}
>
<TouchableOpacity onPress={AppState.closeModal}
style={{borderBottomWidth:1}}>
<Text style={{fontSize:25,textAlign:'right'}}>X</Text>
</TouchableOpacity>
<View style={{padding:10}}>
<Text>This is modal content for screen1</Text>
</View>
</View>
</View>
)}
</View>
)
}

In Individual screens we are checking the “openGlobalModalFor” value from context api to match the current screen from where we are accessing.

For example if we are clicking the button from Screen1.js then the value of “openGlobalModalFor” will be screen1. we are checking this condition and making necessary actions to perform

Screen2.js

import React,{useContext}  from 'react'
import { View, Text } from 'react-native'
import AppContext from '../config/AppContext'
export default function Screen2() {
const AppState = useContext(AppContext)
console.log(AppState) return (
<View
style={{
width:'100%',
height:'100%',
position:'relative',
backgroundColor:AppState.openGlobalModalFor=='screen2' ?
'rgba(0,0,0,0.5)' : 'transparent'
}}>
<Text style={{textAlign:'center',fontSize:35}}>Screen 2</Text>
{
AppState.openGlobalModalFor=='screen2' && (
<View style={{
position:'absolute',
bottom:20,
width:'100%',
height:200,
flexDirection:'row',
justifyContent:'center',
alignItems:'center',
}}
>
<View
style={{
width:'60%',
height:'100%',
backgroundColor:'white',
padding:20,
borderRadius:20
}}>
<Text>This is second screen trigger action</Text>
</View>
<View
style={{
position:'absolute',
bottom:0,
flexDirection:'row',
justifyContent:'center',
width:'100%'
}}
>
<View
style={{
backgroundColor:'white',
width:50,
height:50,
transform:[{rotate:"45deg"}]}}
>
</View>
</View>
</View>
)}
</View>
)}

--

--