How to Style React-Navigation tab navigator with a mobx variable - react-native

I think I've just been staring at this too long, and now my brain doesn't work. I have mobx stores, one of which houses a basic theme color value. I want to pass this into my react-navigation tab background color, but I can't figure out how to do it. We are using Typescript, and that might be part of what is confusing me, because every time I try to inject my store, I get a bunch of errors.
Anyway, if someone could help me figure out how to either inject my store properly, or to pass a prop to my createMaterialTopTabNavigator function, I would greatly appreciate it.
Here is my top tab navigator code:
export const SignedInWithGroup = createMaterialTopTabNavigator(
{
Home: {
screen: MeetingStack,
navigationOptions: {
tabBarLabel: 'Advocacy Day',
tabBarIcon: <Ionicons name="md-home" size={24} />,
},
},
Legislators: {
screen: Legislators,
navigationOptions: {
tabBarLabel: 'Legislators',
tabBarIcon: <Ionicons name="ios-people" size={24} />,
},
},
Messages: {
screen: Messages,
navigationOptions: {
tabBarLabel: 'Messages',
tabBarIcon: <Ionicons name="ios-chatboxes" size={24} />,
},
},
Directory: {
screen: DirectoryStack,
navigationOptions: {
tabBarLabel: 'Directory',
tabBarIcon: <MaterialIcons name="contacts" size={24} />,
},
},
More: {
screen: MoreStack,
navigationOptions: {
tabBarLabel: 'More',
tabBarIcon: <MaterialIcons name="more" size={24} />,
},
},
},
{
tabBarPosition: 'bottom',
tabBarOptions: {
showIcon: true,
style: {
paddingTop: Platform.OS === 'android' ? 0 : 0,
backgroundColor: "#CCBE00", //Replace with theme color
},
tabStyle: {
padding: 0,
},
labelStyle: {
marginTop: Platform.OS === 'ios' ? 8 : 0,
marginBottom: Platform.OS === 'ios' ? 20 : 10,
marginLeft: 15,
fontSize: 7,
color: 'white',
},
},
}
);
export const createRootNavigator = (props:any, signedIn = false) => {
console.log("Props");
console.log(props);
return createSwitchNavigator(
{ SignedInWithGroup: { screen: SignedInWithGroup }, SignedOut },
{ initialRouteName: signedIn ? 'SignedInWithGroup' : 'SignedOut' }
);
};
and here is what is in my app.js:
const Layout = createRootNavigator(signedIn);
return (
<Provider {...stores}>
<Layout />
</Provider>
);

Solution
Use your custom tabbar from MaterialTopTabBar.
Install the react-navigation-tabs module. Because, react-navigation's TabNaviagtion is from react-navigation-tabs. (srcs)
$ yarn install react-navigation-tabs
or
$ npm install react-navigation-tabs --save
Make your custom tabbar component observing store(MobX) for the tabBarComponent option . I do not write code related in MobX or Redux.
import React, { Component } from 'react';
import { MaterialTopTabBar } from 'react-navigation-tabs';
// create a component
class TabBar extends Component {
  render() {
    return (
      <MaterialTopTabBar
        {...this.props}
        style={{ backgroundColor: 'green' }} // Replace with theme color. You should use observing variable from MobX or Redux.
      />
    );
  }
}
//make this component available to the app
export default TabBar;
Apply your custom tabbar in createMaterialTopTabNavigator.
const TabNavigation = createMaterialTopTabNavigator(
  {
    Home: HomeScreen,
    Login: HomeScreen,
    Register: HomeScreen,
  },
  {
    tabBarComponent: (props) => { // Use your custom tabbar here.
return (
<TabBar
{...props}
/>
);
},
tabBarPosition: 'bottom',
tabBarOptions: {
showIcon: true,
style: {
paddingTop: Platform.OS === 'android' ? 0 : 0,
},
tabStyle: {
padding: 0,
},
labelStyle: {
marginTop: Platform.OS === 'ios' ? 8 : 0,
marginBottom: Platform.OS === 'ios' ? 20 : 10,
marginLeft: 15,
fontSize: 7,
color: 'white',
},
},
}
);
Why?
As I wrote, react-navigation-tabs is used as TabNavigation basically in react-navigation And you can use custom view through tabBarComponent option when you createTabNavigator
There are some mudules you can use.
Navigators
createBottomTabNavigator
createMaterialTopTabNavigator
Views
BottomTabBar
MaterialTopTabBar (You used this view)
Utils
createTabNavigator
Thus, extending component or making your custom tabbar from BottomTabBar or MaterialTopTabBar can be one of the easily solutions.

Related

React Native Navigation Title

Apparently simple problem: the Header Title in react Navigation
Navigator file with my Bottom Tabs
const BottomTabNavigator = createMaterialBottomTabNavigator(
{
ToFind: {
screen: TopBarNavigator,
navigationOptions: {
title: "Discover",
tabBarIcon: (tabInfo) => {
return (
<Ionicons
name="md-search"
size={25}
color={tabInfo.tintColor} //prende lo stesso colore di tintcolor giù
/>
);
},
tabBarColor: "#27ae60",
activeColor: "white",
},
},
....
const Navigator = createStackNavigator({
BottomTabNavigator,
Detail: DetailScreen, // not visible but I need the navigation
Item: ItemDisplay, // not visible but I need the navigation
});
Now I try to set the name into the page (at the bottom)
MapScreen.navigationOptions = (navData) => {
return {
headerTitle: "Map",
};
};
Doing this I have the Bottom Tabs styled as I want and navigation but I CAN'T change the header title (navigation title) but I always see BottomTabNavigator
It looks really trick or I'm mistaking somewhere?
Any Idea?
Thanks
createMaterialBottomTabNavigator does not have header bar by default, but createStackNavigator has.
You can do something like this.
import React from "React";
import { createAppContainer, createStackNavigator } from "react-navigation";
import { createMaterialBottomTabNavigator } from "react-navigation-material-bottom-tabs";
class HomeScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Home Screen</Text>
</View>
);
}
}
const Tab1 = createStackNavigator({
S1: {
screen: ToFind
}
});
const Tab2 = createStackNavigator({
S2: {
screen: ToFind
}
});
export default createAppContainer(
createBottomTabNavigator({
Tab1,
Tab2
}, {
//CUSTOM CONFIG
initialRouteName: 'Tab1',
navigationOptions: ({ navigation }) => ({
tabBarIcon: ({ tintColor }) => {
const { routeName } = navigation.state;
let iconName;
if (routeName === 'Tab1') {
iconName = 'icon1';
} else if (routeName === 'Tab2') {
iconName = 'icon2';
}
return <Icon name={iconName} size={24} color={tintColor} />;
<Ionicons
name={iconName}
size={25}
color={tabInfo.tintColor} //prende lo stesso colore di tintcolor giù
/>
},
}),
tabBarOptions: {
activeTintColor: 'white',
inactiveTintColor: 'black',
showLabel: false,
style: {
backgroundColor: '#27ae60',
borderTopWidth: 0,
borderTopColor: '#27ae60',
},
},
});
);
Try these steps. Hope to fix your problem.
Create Your Bottom Tab Navigator :
const BottomTabNavigator = createMaterialBottomTabNavigator(
{
PageOne: {
screen: PageOneComponent,
navigationOptions: {
tabBarIcon: ({ tintColor }) => <Feather name="airplay" size={26} color={tintColor} />,
tabBarLabel: null,
barStyle: { backgroundColor: 'white', elevation: 0, }
},
},
PageTwo: {
screen: PageTwoComponent,
navigationOptions: {
tabBarIcon: ({ tintColor }) => <Feather name="search" size={25} color={tintColor}/>,
tabBarLabel: null,
barStyle: { backgroundColor: 'white', elevation: 0, }
}
},
MapViewLink: {
screen: MapView,
navigationOptions: {
tabBarIcon: <Feather name="map-pin" size={25} color={'green'} />,
tabBarOnPress: ({ navigation }) => {
navigation.navigate('MapView');
},
tabBarLabel: null
}
},
},
{
initialRouteName: 'PageOne',
activeColor: 'orange',
inactiveColor: 'grey',
labeled: false,
barStyle: { backgroundColor: 'white', elevation: 0, borderTopWidth: 1, borderTopColor: '#efefef' },
}
);
Create your StackNavigator and export the navigator
const StackNavigator = createStackNavigator({
// bottom tab navigator
BottomTabNavigator: {
screen: BottomTabNavigator,
navigationOptions: {
header: null
}
},
// MapView Page
MapView: {
screen: MapView,
navigationOptions: ({ navigation }) => ({
title: 'Hello World'
})
},
}, {
defaultNavigationOptions: ({navigation}) => ({
headerTitleAlign: 'center',
cardStyle: { backgroundColor: '#FFFFFF' },
headerTitleStyle: {
// the default styles you want to apply to header title
},
});
export default createAppContainer(StackNavigator);
In the end, put the navigator inside the main project file. e.g App.js

How to make certain tab (screen) skippable in material top bar?

import React from "react";
import { Image } from "react-native";
import { createMaterialTopTabNavigator } from "react-navigation-tabs";
import { createAppContainer } from "react-navigation";
import Profile from "../components/Profile/Profile";
import Trips from "../components/Trips/MyTrips";
import Wallet from "../components/Wallet/Wallet";
import Booking from "../components/Booking/Booking";
import Summary from "../components/Summary/Summary";
import Colors from "../Shared/Colors";
bottomBarConfig = {
Summary: {
screen: Summary,
navigationOptions: {
tabBarIcon: ({ focused }) => {
return (
<Image
source={
focused
? require("./../assets/images/home_active.png")
: require("./../assets/images/home_notactive.png")
}
size={25}
color={focused ? Colors.Primary : "#808080"}
/>
);
},
title: "Home"
}
},
Trips: {
screen: Trips,
navigationOptions: {
tabBarIcon: ({ focused }) => {
return (
<Image
source={
focused
? require("./../assets/images/mytrips_active.png")
: require("./../assets/images/mytrips_notactive.png")
}
size={25}
color={focused ? Colors.Primary : "#808080"}
/>
);
},
style: {}
}
},
Booking: {
screen: () => null,
navigationOptions: {
tabBarIcon: ({ focused }) => {
return (
<Image
source={require("./../assets/images/book.png")}
size={55}
style={{ height: 120 }}
color={focused ? Colors.Primary : "#808080"}
/>
);
},
showLabel: false,
gesturesEnabled: false
}
},
Wallet: {
screen: Wallet,
navigationOptions: {
tabBarIcon: ({ focused }) => {
return (
<Image
source={
focused
? require("./../assets/images/mywallet_active.png")
: require("./../assets/images/mywallet_notactive.png")
}
size={25}
color={focused ? Colors.Primary : "#808080"}
/>
);
}
}
},
Profile: {
screen: Profile,
navigationOptions: {
tabBarIcon: ({ focused }) => {
return (
<Image
source={
focused
? require("./../assets/images/more_active.png")
: require("./../assets/images/more_notactive.png")
}
size={25}
color={focused ? Colors.Primary : "#ff00ff"}
/>
);
}
}
}
};
const TabNavigator = createMaterialTopTabNavigator(bottomBarConfig, {
tabBarPosition: "bottom",
tabBarOptions: {
showIcon: true,
upperCaseLabel: false,
activeTintColor: Colors.Primary,
inactiveTintColor: "gray",
pressColor: Colors.Primary,
style: {
backgroundColor: "white",
border: "none"
},
labelStyle: {
fontSize: 10,
fontFamily: "Montserrat-Regular"
},
indicatorStyle: {
backgroundColor: Colors.Primary
}
}
});
const App = createAppContainer(TabNavigator);
export default App;
This is a photo of what I want to execute(not what I have right now), bottom bar with big button in the middle.
For that I created screen for it and I will remove the label and style the image. But the problem I want user when swipes from tab 2 to tab 3 he actually goes to tab 4 not 3. I want this button to take user to another route.
So I want tab 3 to be skippable, no screen for it, I want it as just a button.
I am using create createMaterialTopTabNavigator because it has swipe feature and I already configured it to be positioned in bottom and I will be editing style of button in middle, just need to make skippable.

HeaderLeft onPress to open the drawer, undefined is not an object

I am having a problem with my line of code specifically with the headerLeft onPress. I wanted to put an icon where when pressed it will open the drawerNavigator of my simple application.
this is my AppNavigation.js
//DRAWER NAVIGATOR
const drawerNav = createDrawerNavigator({
JobFeed: {
screen: MainScreen,
navigationOptions: {drawerLabel: 'Job Feed',}
},
},
{
drawerPosition : "left", contentComponent: CustomDrawerComponent,
});
// Manifest of possible screens
const primaryNav = createStackNavigator({
LaunchScreen: {
screen: LaunchScreen,
navigationOptions: {
title: "Ty, Next",
headerTitleStyle: {
textAlign: 'center',
flex: 1,
fontFamily: 'CoreSansD65Heavy',
color: Colors.semiGray,
}
}
},
MainScreen: {
screen: MainScreen,
navigationOptions: {
title: "Ty, Next",
headerTitleStyle: {
textAlign: 'center',
flex: 1,
fontFamily: 'CoreSansD65Heavy',
color: Colors.semiGray,
marginBottom: 20,
}
}
},
},
{
// Default config for all screens
initialRouteName: 'MainScreen',
})
so here's the problem. In my MainScreen.js I put this code, whenever I press the button It is saying that undefined is not an object (evaluating _this2.props.navigation ) Please help me I am stuck on this particular matter..
static navigationOptions = ({ navigation }) => {
const { state } = navigation;
const {} = state;
return {
headerStyle:{
backgroundColor: "Transparent",
marginRight: 20,
marginLeft: 20,
},
headerLeft: (
<TouchableOpacity onPress={this.props.navigation.openDrawer()}>
<Icon name="bars" color={Colors.red} size={30}/>
</TouchableOpacity>
),
headerLeftStyle: styles.drawerIcon,
headerRight: (
<TouchableOpacity>
<Icon2 name="sc-telegram" color={Colors.red} size={30} />
</TouchableOpacity>
),
headerRightStyle: styles.planeIcon,
headerTransparent: true,
};
}
Also I can't even access my drawer navigator when swiping to right. Any Ideas why is this happening?
In your mainscreen.js is your component is class based component if it is try to create a fat arrow function instead of directly calling on onpress prop and call from that function and if your component is not class based make it first..!

how can I use constructor method with react navigation v3?

I need to use state on my App.js code but react-navigation v3 don't use class so I can't define the constructor method.
Is there any other way so I can use state?
I tried to use a boolean javascript variable but it didn't help.
I use StackNavigator, drawer navigator and DrawerNavigator and BottomTabNavigator like this
const TabAppNavigator = createBottomTabNavigator({
Posts: {
screen: PostsScreen,
navigationOptions: {
tabBarLabel: 'Posts',
tabBarIcon: ({ tintColor }) => (<Icon name="md-home" color={tintColor} size={25} />)
}
},
Tools: {
screen: ToolsScreen,
navigationOptions: {
tabBarLabel: 'Tools',
tabBarIcon: ({ tintColor }) => (<Icon name="md-apps" color={tintColor} size={25} />)
}
},
Favourite: {
screen: FavouriteScreen,
navigationOptions: {
tabBarLabel: 'Favourite',
tabBarIcon: ({ tintColor }) => (<Icon name="md-heart" color={tintColor} size={25} />)
}
},
}, {
initialRouteName: 'Posts',
order: ['Posts', 'Tools', 'Favourite'],
tabBarOptions: {
activeTintColor: '#d94854',
inactiveTintColor: '#818181',
style: {
backgroundColor: '#fff',
borderTopColor: '#818181',
borderTopWidth: 1,
paddingBottom: 5,
paddingTop: 15,
},
labelStyle: {
fontSize: 13,
marginTop: 10,
},
},
navigationOptions: ({ navigation }) => {
return {
headerTitle: 'Growth Hack Toolkit',
headerTintColor: '#fff',
headerStyle: {
backgroundColor: '#d94854',
},
headerLeft: (
<Icon name="md-menu" color="#fff" size={25} style={{ paddingLeft: 15 }} onPress={() => navigation.openDrawer()} />
),
headerRight: (
<Icon name="md-search" color="#fff" size={25} style={{ paddingRight: 15 }} onPress={() => search()} />
)
}
}
}
)
const PostsStackAppNavigator = createStackNavigator({
TabAppNavigator: TabAppNavigator,
Posts: { screen: PostsScreen },
Post: { screen: PostScreen }
})
const ToolsStackAppNavigator = createStackNavigator({
TabAppNavigator: TabAppNavigator,
Tools: { screen: ToolsScreen },
Tool: { screen: ToolScreen },
ToolList: { screen: ToolListScreen },
Web: { screen: WebScreen },
Mobile: { screen: MobileScreen },
})
const DrawerAppNavigator = createDrawerNavigator({
Posts: { screen: PostsStackAppNavigator },
Tools: { screen: ToolsStackAppNavigator },
About: { screen: AboutScreen },
}, {
contentComponent: SideMenu,
drawerWidth: 250,
})
const App = createAppContainer(DrawerAppNavigator);
export default App;
I want to change my header view based on my state
basically, I have a default header (the same header for all tab screens) that contains a title, menu icon to open the drawer navigation and search icon to start searching
what I need to do is that when search icon is pressed I will change my state to show the instead of the title and when close icon is pressed I will change the state to show my default header again.
after spending so much time searching for a solution or a workaround, the answer is: I cannot.
It is not possible to have state in a functional component. So I have to use react navigation v2 instead of v3 as it is implemented as class-based component.
These links could help to understand:
difference between functional and class-based React components
React State without Constructor

How can I conditionally set tabs in createBottomTabNavigator?

I have a static data file (json) that stores information like facebook page link. If the facebook page link is empty, I do NOT want the Follow Us tab to show. The facebook page link is stored in businessDataJSON.facebook. Is there a way to only show the Follow Us Tab if businessDataJSON.facebook is not empty (empty string)? This is my App.js:
const ListStack = createStackNavigator(
{
ListCategories: ListCategoriesScreen,
ListCategoryItems: ListCategoryItemsScreen
},
{
initialRouteName: "ListCategories",
defaultNavigationOptions: {
headerStyle: {
backgroundColor: businessDataJSON.theme.navigationBarBackground
},
headerTintColor: businessDataJSON.theme.navigationBarTint,
headerTitleStyle: {
fontWeight: "bold"
}
}
}
);
const FollowUsStack = createStackNavigator(
{
FollowUs: FollowUsScreen
},
{
initialRouteName: "FollowUs",
defaultNavigationOptions: {
headerStyle: {
backgroundColor: businessDataJSON.theme.navigationBarBackground
},
headerTintColor: businessDataJSON.theme.navigationBarTint,
headerTitleStyle: {
fontWeight: "bold"
}
}
}
);
export default createAppContainer(
createBottomTabNavigator(
{
ListTab: {
screen: ListStack,
navigationOptions: {
title: businessDataJSON.theme.tabBarListTitle,
tabBarIcon: ({ tintColor }) => (
<Icon
name={businessDataJSON.theme.tabBarListIcon}
size={17}
color={tintColor}
/>
)
}
},
FollowUsStack: {
screen: FollowUsStack,
navigationOptions: {
title: "Follow Us",
tabBarIcon: ({ tintColor }) => (
<Icon name="users" size={17} color={tintColor} />
)
}
}
},
{
tabBarOptions: {
activeTintColor: businessDataJSON.theme.tabBarIconActive,
inactiveTintColor: businessDataJSON.theme.tabBarIconInactive,
style: {
elevation: 8,
...Platform.select({
ios: { paddingTop: 4, paddingBottom: 4 },
android: {
borderTopWidth: 0,
paddingTop: 6,
paddingBottom: 6
}
})
}
}
}
)
);
Yes but you would need to make the creation of the tabs dinamic wich leads to the navigation prop to be lost and make a nightmare to use it without redux. The simple approach that i can see is to use a root createSwitchNavigator with a loading screen/start screen and the tabs as the 2nd screen. in the loading screen you check if there is your facebook page, and navigate to them accordingly. this example is for auth, but it works in your case . trust me, the navigation props is a nightmare to manage.