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:-
Related
I want the onboarding screen to show only on the first launch of the App. I've implement the OnboardScreen and AuthScreen. I've implement AsyncStorage with If Else statement to check if the user has launched the App for the first time or not. The problem I have is that when the app is closed and then opened again the OnboardScreen is shown again.
I'm using React Native Expo to run the application.
App.tsx
import OnboardScreen from "./src/screens/OnboardScreen/OnboardScreen";
import AuthScreen from "./src/screens/AuthScreen/AuthScreen";
import { NavigationContainer } from "#react-navigation/native";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import AsyncStorage from '#react-native-async-storage/async-storage';
import { useEffect, useState } from "react";
const Stack = createNativeStackNavigator();
const App = () => {
const [loading, setLoading] = useState(true);
const [isFirstTimeLoad, setIsFirstTimeLoad] = useState(false)
//AsyncStorage
const checkForFirstTimeLoaded = async () => {
const result = await AsyncStorage.getItem('isFirstTimeOpen')
if(result == null) setIsFirstTimeLoad(true);
setLoading(false)
}
useEffect(() => {
checkForFirstTimeLoaded()
}, [])
if(loading) return null
if(isFirstTimeLoad) return (
<NavigationContainer>
<Stack.Navigator screenOptions={{
headerShown: false
}}>
<Stack.Screen
name="OnboardScreen"
component={OnboardScreen}
options={{ title: "Onboard", headerShown: false}}
/>
<Stack.Screen
name="AuthScreen"
component={AuthScreen}
options={{ title: "Authentication", headerShown: false}}
/>
</Stack.Navigator>
</NavigationContainer>
);
if(!isFirstTimeLoad) return <AuthScreen/>
}
export default App
Onboard.js
import React from "react";
import { View, Text, Button, StyleSheet, Image, TouchableOpacity } from "react-native";
import Onboarding from "react-native-onboarding-swiper";
const Skip = ({...props}) => (
<TouchableOpacity
style={{
backgroundColor: "#F4F4F4",
borderRadius: 10,
width: 90,
height: 40,
padding: 5,
alignItems: "center",
left: 25}}
{...props}
>
<Text style={{fontSize: 18}}>Skip</Text>
</TouchableOpacity>
)
const Next = ({...props}) => (
<TouchableOpacity
style={{
backgroundColor: "#258671",
borderRadius: 10,
width: 90,
height: 40,
padding: 5,
alignItems: "center",
right: 25}}
{...props}
>
<Text style={{fontSize: 18}}>Next</Text>
</TouchableOpacity>
)
const Done = ({...props}) => (
<TouchableOpacity
style={{
backgroundColor: "#258671",
borderRadius: 10,
width: 90,
height: 40,
padding: 5,
alignItems: "center",
right: 25}}
{...props}
>
<Text style={{fontSize: 18}}>Done</Text>
</TouchableOpacity>
);
const Dots = ({selected}) => {
let backgroundColor;
backgroundColor = selected ? "rgba(37, 134, 113, 1)" : "rgba(185, 185, 185, 1)"
return (
<View
style={{
width: 6,
height: 6,
borderRadius: 3,
marginHorizontal: 3,
backgroundColor
}}
/>
);
}
const OnboardScreen = ({ navigation }) => {
return (
<>
<Onboarding
SkipButtonComponent={Skip}
NextButtonComponent={Next}
DoneButtonComponent={Done}
DotComponent={Dots}
onSkip={() => navigation.replace("AuthScreen")}
onDone={() => navigation.navigate("AuthScreen")}
titleStyles={{ color: 'blue' }}
pages={[
{
backgroundColor: '#F2F2F2',
image: <Image source={require('../../assets/images/onboard-5.png')} />,
title: 'A.I. Powered',
subtitle: 'Done with React Native Onboarding Swiper',
},
{
backgroundColor: '#F2F2F2',
image: <Image source={require('../../assets/images/onboard-5.png')} />,
title: 'Onboarding 2',
subtitle: 'Done with React Native Onboarding Swiper',
},
{
backgroundColor: '#F2F2F2',
image: <Image source={require('../../assets/images/onboard-5.png')} />,
title: 'Onboarding 3',
subtitle: 'Done with React Native Onboarding Swiper',
},
{
backgroundColor: '#F2F2F2',
image: <Image source={require('../../assets/images/onboard-5.png')} />,
title: 'Onboarding 4',
subtitle: 'Done with React Native Onboarding Swiper',
},
]}
/>
</>
);
};
export default OnboardScreen
Set isFirstTimeOpen to false after onboarding is shown. Note that Async Storage can only store string data.
await AsyncStorage.setItem('isFirstTimeOpen', 'false')
I am creating Drawer in react native using Drawer.Navigator. But the issue is that how to move to Login screen if click on Logout button?
Any suggestion? my code is below. All remaining code is working fine. I only want to navigate the login screen by clicking on logout button.
import * as React from 'react';
import { Button, View, Text, TouchableOpacity } from 'react-native';
import {
createDrawerNavigator, DrawerContentScrollView,
DrawerItemList,
DrawerItem,
} from '#react-navigation/drawer';
import { NavigationContainer } from '#react-navigation/native';
import Resources from '../DrawerScreens/Resources';
import Themes from '../DrawerScreens/Themes';
import AboutUs from '../DrawerScreens/AboutUs';
import CustomSidebarMenu from './CustomSidebarMenu';
import Login from '../Validation/Login';
const Drawer = createDrawerNavigator();
export default function DrawerSetting({ navigation }) {
return (
<NavigationContainer independent={true} >
<Drawer.Navigator initialRouteName="Resources" drawerContent={(props) => <CustomSidebarMenu {...props} />}
>
<Drawer.Screen name="Resources" component={Resources}
options={{ drawerLabelStyle: { fontSize: 16, color: 'black', } }}
/>
<Drawer.Screen name="Themes" component={Themes}
options={{ drawerLabelStyle: { fontSize: 16, color: 'black', } }}
/>
<Drawer.Screen name="About Us" component={AboutUs}
options={{ drawerLabelStyle: { fontSize: 16, color: 'black', } }}
/>
</Drawer.Navigator>
</NavigationContainer>
);
}
I used to create Another component for creating the logout button,
import React from 'react';
import {
SafeAreaView,
View,
StyleSheet,
Image,
Text,
Linking,
TouchableOpacity,
BackHandler
} from 'react-native';
import {
DrawerContentScrollView,
DrawerItemList,
DrawerItem, Drawer,
} from '#react-navigation/drawer';
const CustomSidebarMenu = (props) => {
const BASE_PATH = '';
function logout(){
alert("Hello");
// props.navigation.navigate("Login");
// BackHandler.exitApp();
}
return (
<SafeAreaView style={{ flex: 1 }}>
{/*Top Large Image */}
<Image
source={require("../../assets/images/g_logo_blue.png")}
style={styles.sideMenuProfileIcon}
/>
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
{/* { <DrawerItem
label="Visit Us"
onPress={() => {props.navigation.navigate('Login')}}
/> } */}
{/* <View style={styles.customItem}>
<Text
onPress={() => {
Linking.openURL('https://aboutreact.com/');
}}>
Rate Us
</Text>
<Image
source={{ uri: BASE_PATH + 'star_filled.png' }}
style={styles.iconStyle}
/>
</View> */}
<TouchableOpacity onPress={logout}>
<Text style={{ fontSize: 16, fontWeight: 'bold', textAlign: 'left', color: 'blue', marginLeft: 20, }}>
Logout
</Text>
</TouchableOpacity>
</DrawerContentScrollView>
<Text style={{ fontSize: 16, textAlign: 'center', color: 'blue' }}>
https://www.glocoach.com/
</Text>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
sideMenuProfileIcon: {
// resizeMode: 'cover',
width: "70%",
height: 70,
marginTop: 20,
marginBottom: -40,
marginLeft: -50,
// borderRadius: 100 / 2,
alignSelf: 'center',
},
iconStyle: {
width: 15,
height: 15,
marginHorizontal: 5,
},
customItem: {
padding: 16,
flexDirection: 'row',
alignItems: 'center',
},
});
export default CustomSidebarMenu;
Normally what I would do in this situation is have two navigators. One for the logged-out screens and one for the logged-in screens. Then I would bind this to a check-in JSX like:
<NavigationContainer>
{!isLoggedIn ? (<LoggedOutNavigator />) : (<LoggedInNavigator />)}
</NavigationContainer>
After that, all you have to do when the button is pressed is to change the isLoggedIn value. you can save isloggedIn in redux and then just dispatch an action to handle the change.
if you only have one navigator and really don't want to change it, then you can add a useEffect hook to navigate to the log in screen once isLoggedIn is set to false.
I need to add an indicator for the active tab I tried to add a borderBottom with tabStyle but we can't check focused with that.
Using react-navigation v5 and createBottomTabNavigator for bottom tabs.
Here's my code:
<BottomTab.Navigator
tabBarOptions={{
activeTintColor: colors.brown,
labelPosition: 'below-icon',
}}>
<BottomTab.Screen
name="Home"
component={HomeTabNav}
options={{
tabBarLabel: 'Home',
tabBarIcon: ({focused}) => {
return focused ? (
<HomeSelectedIcon height={ms(24)} width={ms(24)} />
) : (
<HomeIcon height={ms(24)} width={ms(24)} />
);
},
}}
/>
...
</BottomTab.Navigator>
);
};
Thanks in advance!
I figured it out myself by making a custom tabbar icon if someone needs to achieve this using the bottom-tab bar only.
Here's the code.
<BottomTab.Navigator
tabBarOptions={{
activeTintColor: colors.brown,
showLabel: false,
tabStyle: styles.tabStyle,
style: styles.tabContainerStyle,
}}>
<BottomTab.Screen
name="Home"
component={HomeTabNav}
options={{
tabBarLabel: 'Home',
tabBarIcon: ({focused}) => {
return focused ? (
<View style={styles.labelFocusedContainer}>
<HomeSelectedIcon height={24} width={24} />
<Text style={styles.labelFocusedStyle}>Home</Text>
</View>
) : (
<View style={styles.labelContainer}>
<HomeIcon height={24} width={24} />
<Text style={styles.labelStyle}>Home</Text>
</View>
);
},
}}
/>
...
</BottomTab.Navigator>
const styles = StyleSheet.create({
labelContainer: {
alignItems: 'center',
width: '100%',
},
labelFocusedContainer: {
alignItems: 'center',
width: '100%',
borderBottomWidth: 3,
borderBottomColor: colors.brown,
},
labelFocusedStyle: {
textAlign: 'center',
marginVertical: 8,
color: colors.brown,
backgroundColor: 'transparent',
fontSize: 10,
},
labelStyle: {
textAlign: 'center',
marginVertical: 8,
color: colors.veryDarkgray,
backgroundColor: 'transparent',
fontSize: 10,
},
});
But the best and easy way to do this is by using createMaterialTopTabNavigator and using these props.
tabBarPosition="bottom"
tabBarOptions={{
showIcon: true,
pressOpacity: 1,
iconStyle: styles.iconStyle,
showLabel: true,
activeTintColor: colors.brown,
indicatorStyle: {
borderWidth: 2,
borderColor: colors.brown,
},
This does not seem to be possible / easily achievable with bottom-tabs, but you could use the material version - #react-navigation/material-top-tabs and configure it to match your needs, specifically using tabBarPosition="bottom" and tabBarOptions={{ indicatorStyle: { backgroundColor } }}.
You can check more options in the docs: https://reactnavigation.org/docs/material-top-tab-navigator/#tabbaroptions
import * as React from 'react';
import { Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createMaterialTopTabNavigator } from '#react-navigation/material-top-tabs';
const Tabs = createMaterialTopTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tabs.Navigator tabBarPosition="bottom" tabBarOptions={{ indicatorStyle: { backgroundColor: 'red' } }}>
<Tabs.Screen name="screen 1" component={View} />
<Tabs.Screen name="screen 2" component={View} />
</Tabs.Navigator>
</NavigationContainer>
);
}
The best answer would be to use the tabBarButton prop to override and add your own custom styles to the container of the tab button.
https://reactnavigation.org/docs/bottom-tab-navigator#tabbarbutton
const CustomTabButton = (props) => (
<Pressable
{...props}
style={
props.accessibilityState.selected
? [props.style, styles.activeTab]
: props.style
}
/>
)
styles.activeTab is the custom style you want to add, be careful to spread the props.style to get the default styles from the library like width, padding, height etc
props.accessibilityState.selected will add styles according to condition if you want styles for all the tabs you can remove the condition.
Inside screeenOptions prop on navigator or the option props of each screen.
tabBarButton: CustomTabButton
Using material top tab is not a good solution because it does not support well with a keyboard. But bottom tabs do work well with the keyboard.
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
I'm using react-native-status-bar-alert in combination with react-navigation.
In the latest version of react-navigation there's a problem with the height.
The height of the header is too big when the alert is active.
Anyone else experienced this issue and has a solution?
I think you should use a plugin: navigationbar-react-native
First, if you use react-navigation you should hide header-bar and use custom header-bar
export const RootStack = createStackNavigator(
{
Home: {
screen: HomeComponent,
navigationOptions: {
header: null,
},
},
},
{
initialRouteName: 'Home',
}
);
1, Install package
npm i navigationbar-react-native --save
2, Using
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,Image,
View,
TouchableOpacity,
} from 'react-native';
import NavigationBar from 'navigationbar-react-native';
const ComponentLeft = () => {
return(
<View style={{ flex: 1, alignItems: 'flex-start'}} >
<TouchableOpacity style={ {justifyContent:'center', flexDirection: 'row'}}>
<Image
source={require('./img/ic_back.png')}
style={{ resizeMode: 'contain', width: 20, height: 20, alignSelf: 'center' }}
/>
<Text style={{ color: 'white', }}>Back Home</Text>
</TouchableOpacity>
</View>
);
};
const ComponentCenter = () => {
return(
<View style={{ flex: 1, }}>
<Image
source={require('./img/ic_logo.png')}
style={{resizeMode: 'contain', width: 200, height: 35, alignSelf: 'center' }}
/>
</View>
);
};
const ComponentRight = () => {
return(
<View style={{ flex: 1, alignItems: 'flex-end', }}>
<TouchableOpacity>
<Text style={{ color: 'white', }}> Right </Text>
</TouchableOpacity>
</View>
);
};
class App extends Component {
render() {
return (
<View style={styles.container}>
<NavigationBar
componentLeft = { () => {<ComponentLeft /> }
componentCenter = { () => {<ComponentCenter /> }
componentRight = { () => {<ComponentRight /> }
navigationBarStyle= {{ backgroundColor: ''#215e79'' }}
statusBarStyle = {{ barStyle: 'light-content', backgroundColor: '#215e79' }}
/>
</View>
);
}
}
Easy create custom header bar in react native