How to style TabNavigator's Tabs? - reactjs

I have such an ugly looking default Tabs for TabNavigator. How can I add styles on it like Instagram?
I want to set the Tabs Navigator's background as white (not grey) and set the border's top with line. Plus, I want to set the active icon color as black. (JUST LIKE THE INSTAGRAM..!)
I'm not sure where I can put these styles on my TabNavigator.
export default TabNavigator(
{
Feed: {
screen: FeedStackNavigator,
},
...
},
{
navigationOptions: ({ navigation }) => ({
header: null,
tabBarIcon: ({ focused }) => {
const { routeName } = navigation.state;
let iconName;
switch (routeName) {
case 'Feed':
iconName = Platform.OS === 'ios'
? `ios-information-circle${focused ? '' : '-outline'}`
: 'md-list-box';
break;
}
Thanks! :)

react-navigation has lots of different properties for styling TabNavigator. You can use them to style your icons and TabBar itself.
Example
tabBarOptions: {
activeTintColor: '#e91e63',
labelStyle: {
fontSize: 12,
},
style: {
backgroundColor: 'blue',
},
}

Related

How to have a unique icon for two screens with react navigation

I have a bottom tab navigator in react native and I put my screens into it this way :
const AppStack = createBottomTabNavigator(
{
FirstPage : {
screen: FirstPage,
navigationOptions: {
tabBarVisible: true,
}
},
SecondPage : {
screen: SecondPage,
navigationOptions: {
tabBarVisible: true,
tabBarButtonComponent: () => false
}
},
ThirdPage : {
screen: ThirdPage,
navigationOptions: {
tabBarVisible: true,
}
},
},
{
defaultNavigationOptions: ({navigation}) => ({
tabBarIcon: ({focused}) => {
if (navigation.state.routeName === 'FirstPage' || navigation.state.routeName === 'SecondPage') {
icon = focused ? require('iconPathFocused.png') : require('iconPathNotFocused.png)
} else if (navigation.state.routeName === 'ThirdPage') {
[...]
}
return <TabIcon path={icon}/>
}
})
}
)
The problem is that when I'm on the SecondPage screen, the tabbar is still visible but none of the icons are "hilighted" because not focused.
The thing is I don't want any particular icon to be displayed for the second page. I want it to be like a child of the First page so when I navigate from the First page to the Second one, the same icon (still FirstPage's one so) is displayed and highlighted.
Thank you very much !
You can set navigationOptions something like this with tabBarIcon for all your screens
So you can set different focused and not focused icons for each and every tab.
FirstPage : {
screen: FirstPage,
navigationOptions: {
tabBarLabel: "First Page",
tabBarIcon: ({ tintColor, focused }) => (
<Image source={focused ? require('iconPathFocused.png') : require('iconPathNotFocused.png')} style={{height: 28, width: 28}}/>
),
}
},
SecondPage : {
screen: SecondPage,
navigationOptions: {
tabBarLabel: "Second Page",
tabBarIcon: ({ tintColor, focused }) => (
<Image source={focused ? require('iconPathFocused.png') : require('iconPathNotFocused.png')} style={{height: 28, width: 28}}/>
),
}
},
Add this to bellow defaultNavigationOptions
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
}
this might help.
For more details have help from React-navigation official page : https://reactnavigation.org/docs/en/tab-based-navigation.html
Check customise appearance there that might help further.

react-navigation title gets truncated

I am having trouble with the stacknavigator showing the title on the header bar without truncating it, even though there are no other elements on the header.
const Tabs = createBottomTabNavigator({
Home,
Bookings,
UserCenter,
Help,
WhatsApp
}, {
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
},
tabBarPosition: 'bottom',
animationEnabled: false,
swipeEnabled: false,
navigationOptions: {
tabBarOnPress: ({navigation, defaultHandler}) => {
if (navigation.state.routeName === 'WhatsApp') {
Linking.openURL('whatsapp://send?phone=982')
defaultHandler = () => {}
} else {
defaultHandler()
}
}
}
});
Tabs.navigationOptions = ({ navigation }) => {
const { routeName } = navigation.state.routes[navigation.state.index];
// You can do whatever you like here to pick the title based on the route name
const options = {
headerTitle: routeName
}
if (routeName === 'Home') {
options.header = null
}
return options
};
const App = createStackNavigator({
Tabs,
Wallet,
HelpQuestion,
Service,
Category,
BookNow,
Favorites,
BookingSummary,
Referral,
Search
}, {
navigationOptions: {
headerTintColor: '#000'
}
})
Here's what I get
As you can notice, the help tab has no other buttons, or anything to take up the space, and yet the title is truncated
If you're not setting a font anywhere, it's probably your phone's system font not playing nicely with React Native. For example I ran into this problem on my OnePlus 5T using the OnePlus Slate font.
Change your app's font to use a standard font like Roboto for Android and Arial for iOS.
See here for more info and how to fix it: https://github.com/react-navigation/react-navigation/issues/5050

Label of createBottomTabNavigator not showing - React Native

So I pulled one of my react native projects from a few months ago and While using it I discovered that TabNavigator was deprecated. So instread I am now using createBottomTabNavigator like they suggested. It seems to work out of the box, but for some reason my labels are not showing, only the icons in the tabs.
I was looking at the docs and it was saying it is actually default true for showing the label. For some reason the label just won't show up. Also tried to set the showLabel to true but that didn't do anything else. Someone with the same problem that got a fix?
My code
import React from 'react';
import { Text, View, Button, StyleSheet } from 'react-native';
// Icons for tabs
import I from 'react-native-vector-icons/MaterialCommunityIcons';
// Navigation dep
import { createBottomTabNavigator, createStackNavigator } from 'react-navigation';
// Custom components/screens
import HomeScreen from './screens/HomeScreen'
import FavoritesScreen from './screens/FavoritesScreen'
import AccountScreen from './screens/AccountScreen'
// Custom styles
import HeaderBar from './styles/HeaderBar'
// Tabs navigation
const HomeStack = createStackNavigator({
Home: HomeScreen
});
const FavoritesStack = createStackNavigator({
Favorites: FavoritesScreen
});
const AccountStack = createStackNavigator({
Account: AccountScreen
});
export default createBottomTabNavigator(
{
Home: HomeStack,
Favorites: FavoritesStack,
Account: AccountStack,
},
{
navigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, tintColor }) => {
const { routeName } = navigation.state;
let iconName;
if (routeName === 'Home') {
iconName = `tag${focused ? '' : '-outline'}`;
} else if (routeName === 'Favorites') {
iconName = `heart${focused ? '' : '-outline'}`;
} else {
iconName = `account-box${focused ? '' : '-outline'}`;
}
return <I name={iconName} size={25} color={tintColor} />;
},
tabBarLabel: () => {
const { routeName } = navigation.state;
return routeName.toUpperCase();
}
}),
tabBarOptions: {
activeTintColor: '#E95736',
inactiveTintColor: 'gray',
labelStyle: {
fontSize: 9,
fontFamily: 'Rubik-Medium'
},
style: {
backgroundColor: '#FAF8F8',
borderTopWidth: 0.5
},
},
}
);
I think the issue is related to tabBarLabel and I believe the problem will fix itself if you remove that bit. You can easily achieve the same effect by just making the label text upper case where you declare the route.

Nested TabNavigator inside StackNavigator: controlling the header

I have a setup much like this:
let Tabs = createBottomTabNavigator({
screen1: Screen1,
screen2: Screen2
})
let Stack = createStackNavigator({
tabs: Tabs
otherScreen: OtherScreen
})
The stack navigator has a header, and that's fine. What I want is to get different header icons depending on what tab I'm currently on.
I'm using the following versions:
"react": "16.3.1",
"react-native": "~0.55.2",
"react-navigation": "^2.2.5"
I've considered switching up my setup so that each tab screen has its own StackNavigator but I like having the sliding animation when switching tabs and I don't want the header icons to slide along. The header bar should remain static but simply show different icons depending on the current tab.
You can use like this below, https://reactnavigation.org/docs/en/stack-navigator.html
//Screen1 Stack.
const Screen1 = createStackNavigator ({
Home: {
screen: Home,
navigationOptions: {
header: null //Need to set header as null.
}
}
});
//Screen2 Stack
const Screen2 = createStackNavigator ({
Profile: {
screen: Profile,
navigationOptions: {
header: null //Need to set header as null.
}
}
});
let Tabs = createMaterialTopTabNavigator({
Screen1:{
screen: Screen1 //Calling Screen1 Stack.
},
Screen2:{
screen: Screen2 //Calling Screen2 Stack.
}
},{ tabBarPosition: 'bottom' }) //this will set the TabBar at Bottom of your screen.
let Stack = createStackNavigator({
tabs:{
screen: Tabs, //You can add the NavigationOption here with navigation as parameter using destructuring.
navigationOptions: ({navigation})=>{
//title: (navigation.state.routes[navigation.state.index])["routeName"]
//this will fetch the routeName of Tabs in TabNavigation. If you set the routename of the TabNavigation as your Header.
//use the following title property,this will fetch the current stack's routeName which will be set as your header in the TabBar.
//title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName
//you can use switch case,on matching the route name you can set title of the header that you want and also header left and right icons similarly.
switch ((navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName) {
case "Screen1":
return {
title: "Home",
headerLeft: (<Button
onPress={()=> alert("hi")}
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
case "Screen2":
return {
title: "Profile",
headerLeft: (<Button
onPress={()=> alert("hi")}
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
default:
return { title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName }
}
}
},
otherScreen:{
screen: OtherScreen
}
})
//navigationOptions
navigationOptions: ({navigation})=>{
//title: (navigation.state.routes[navigation.state.index])["routeName"]
//this will fetch the routeName of Tabs in TabNavigation. If you set the routename of the TabNavigation as your Header.
//use the following title property,this will fetch the current stack's routeName which will be set as your header in the TabBar.
//title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName
switch ((navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName) {
case "Screen1":
return {
title: "Home",
headerLeft: (<Button
onPress={()=> alert("hi")} //Here you can able to set the back behaviour.
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
case "Screen2":
return {
title: "Profile",
headerLeft: (<Button
onPress={()=> alert("hi")}
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
default:
return { title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName }
}
}
//alert(navigation.state)
{
"routes":[
{
"key":"Screen1",
"routeName":"Screen1",
"routes":[
{
"key":"Home",
"routeName":"Home",
}
],
"index":0,
"isTransitioning":false,
"key":"id-1530276062643-0"
},
{
"key":"Screen2",
"routeName":"Screen2",
"routes":[
{
"key":"Profile",
"routeName":"Profile",
}
],
"index":0,
"isTransitioning":false,
"key":"id-1530276062643-0"
}
],
"index":0,
"isTransitioning":false,
"routeName":"tabs",
"key":"id-1530276062643-0"
}
//(navigation.state.routes[navigation.state.index])["routeName"]
//(navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName
this will give the current route name of the tab inside StackNavigation.
The above code will set the title in root stack header where the TabBar resides as first route,so we are setting the header as null for the individual stack in TabBar. By using this way will provide Animation while switching the screens in TabBar since header will remain static.
you can find the working copy here https://www.dropbox.com/s/jca6ssn9zkzh9kn/Archive.zip?dl=0
Download this and Execute the following.
npm install //to get dependencies
react-native upgrade //to get android and ios folder
react-native link //to link dependencies and libraries
react-native run-ios (or) react-native run-android
you can use the above, Let me know if any.
You can achieve the desired behavior with the current navigation stack configuration of yours. You might need to change couple of things and combine couple of properties but it is fairly simple when you get yor head around it.
I try to explain it with a small example.
Consider having below navigators;
const Tabs = createBottomTabNavigator({
screen1: Tab1,
screen2: Tab2
})
const Stack = createStackNavigator({
tabs: {
screen: TabsPage,
navigationOptions: ({navigation}) => {
return { title: (navigation.state.params && navigation.state.params.title ? navigation.state.params.title : 'No Title' ) }
}
},
otherScreen: Page
})
As you can see I am setting the title parameter from the navigation state. To be ale to set the param for that navigator, we are going to get help from screenProps property;
class TabsPage extends Component {
onTabsChange = (title) => {
this.props.navigation.setParams({ title })
}
render() {
return(<Tabs screenProps={{ onTabsChange: this.onTabsChange }} />)
}
}
I created a wrapper component for tab navigator and passed down a function which is setting the title parameter.
For the last part we need to know how and when to use that function we passed down. For that we are going to use addListener navigation prop
class Tab1 extends React.Component {
setTitle = () => {
this.props.screenProps.onTabsChange('Title from Tab 1')
}
componentDidMount() {
this.props.navigation.addListener('willFocus', this.setTitle)
}
render() {
return <View><Text>{'Tab1'}</Text></View>
}
}
When our tab focused the passed function will run and then set the title for that tab. You can use this process for setting different buttons or icons for the header. You can find the working snack here.
const RootStack = createStackNavigator(
{
Home: {screen: HomeScreen},
FilterScreen: createMaterialTopTabNavigator({
Tab1: {screen:Tab1Screen},
Tab2: {screen: Tab2Screen},
}),
},
{
mode: 'modal',
headerMode: 'none',
}
);
render() {
return <RootStack/>;
}
In AppNavigation.js //or where you have defined your routes.
let Tabs = createBottomTabNavigator({
screen1: Screen1,
screen2: Screen2
})
let Stack = createStackNavigator({
tabs: Tabs
otherScreen: OtherScreen
},{
headerMode:"float", //Render a single header that stays at the top and animates as screens are changed. This is a common pattern on iOS.
headerTransitionPreset:"fade-in-place" //this will give a slight transition when header icon change.
}
)
In Screen1.js
class Screen1 extends Component {
static navigationOptions = ({ navigation }) => {
return {
...
headerLeft: <HeaderLeftImage navigation={navigation} image={"Image_For_Screen1"}/>,
...
}
}
...
}
NOTE: You can add title, headersStyle,headerRight in same way read this link header configuration for more detail
In Screen2.js do similar to Screen1
class Screen2 extends Component {
static navigationOptions = ({ navigation }) => {
return {
...
headerLeft: <HeaderLeftImage navigation={navigation} image={"Image_For_Screen2"}/>,
...
}
}
...
}
Header.js
export const HeaderLeftImage = (props) => (
<View style={{
'add styles'
}}>
<TouchableOpacity onPress={() => {"Add action here" }}>
<Image source={props.image} resizeMethod='resize' style={{ height: 30, width: 90, resizeMode: 'contain' }} />
</TouchableOpacity>
</View>
)
Hope this helps, if you have any doubt regarding the code feel free to ask.
If you are using react navigation <2 i.e ~1.5.* You can set it like this.
const Tabs = TabNavigator({
Tab1:{
screen: Tab1,
navigationOptions: ({navigation}) => {
return { title: "Tab 1 Heading", tabBarLabel:"Tab 1 "}
},
}
Tab2:{
screen: Tab2
navigationOptions: ({navigation}) => {
return { title: "Tab 2 Heading", tabBarLabel:"Tab 2 "}
}
}
})
const Stack = StackNavigator({
tabs: {
screen: Tabs
navigationOptions: ({navigation}) => {
return { title: "Stack"}
}
},
otherScreen: Page
})
I'm not sure why did they remove this feature, when you try the same it won't work on the latest react-navigation. Now title object key is used for fallback it seems.
This may be helpful some users. If you wish you can try downgrading also.
I have the Upgraded the React Navigation for my project I think this way will be useful to someone
const Tabs = TabNavigator({
Tab1:{
screen: Tab1,
navigationOptions: ({navigation}) => {
return { tabBarLabel:"Tab 1 "}
}},
Tab2:{
screen: Tab2
navigationOptions: ({navigation}) => {
return { tabBarLabel:"Tab 2 "}
}}
});
Tabs.navigationOptions = ({navigation})=>{
const { routeName } = navigation.state.routes[navigation.state.index]; //This gives current route
switch(routeName){
case "Tab1":
headerTitle="Tab 1";
break;
case "Tab1":
headerTitle="Tab 1";
break;
}
return {
headerTitle: headerTitle
}
}
You can simply use property headerShown: false. I have created a working example of react-navigation version: 5.x.x, nesting different navigators: stack, drawer & bottom-tabs.
Here's the link to github:
https://github.com/mvpbuddy/react-native-nested-navigators/
For a more detailed overview, you can checkout my blog:
https://mvpbuddy.io/blog/detail/how-to-build-an-app-with-nested-stack-drawer-bottom-tab-navigators

States in TabStackNavigator?

It seems like TabNavigator doesn't have it's own state. Is there any way to use state or props?
I want to show the number of unread notification on the Notification TabIcon.
export default TabNavigator(
{
...
Noti: {
screen: NotificationStackNavigator,
},
...
},
{
navigationOptions: ({ navigation }) => ({
header: null,
tabBarIcon: ({ focused }) => {
const { routeName } = navigation.state;
let iconName;
switch (routeName) {
...
case 'Noti':
iconName = 'ios-notifications';
break;
...
}
...
if(iconName == 'ios-notifications') {
return (
<IconBadge
MainElement={
<Ionicons
name={iconName}
size={30}
style={{ marginBottom: -3 }}
color={focused ? Colors.tabIconSelected : Colors.tabIconDefault}/>
}
BadgeElement={
<Text style={{color:'#FFFFFF'}}>
{{this.state.notifications}} // my goal
</Text>
}
IconBadgeStyle={{
backgroundColor: '#f64e59',
position: 'absolute',
right:-10,
top:0,
}}
/>
);
}
...
Please let me know if anything is unclear. Thanks in advance
UPDATE I'm planning to refactor my TabNavigator. Is this what you're trying to say?
export default TabNavigator(
to
const MainTabNavigator = TabNavigator(
class MainTabNavigator extends Component {
state = {notification}
export default connect(...)(MainTabNavigator);
UPDATE 2 MainTabNavigator's Top Level component is another TabNavigator. Is there any other solution?
const RootTabNavigator = TabNavigator ({
Auth: {
screen: AuthStackNavigator,
},
Welcome: {
screen: WelcomeScreen,
},
Main: {
screen: MainTabNavigator,
},
You can pass additional custom props via screenprops
const TabNav = TabNavigator({
// config
});
<TabNav
screenProps={/* this prop will get passed to the screen components as this.props.screenProps */}
/>
The full documentation is here

Resources