react-navigation title gets truncated - reactjs

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

Related

Two headers when using nested Stacknavigators

In my react native app I am using TabNavigator inside a StackNavigator as so:
const AppTabs = createBottomTabNavigator(
{
Home: Tab1,
Create: Tab2,
Search: Tab3,
Ask: Tab4
}
);
const AppStack = createStackNavigator(
{
Tabs: AppTabs,
Screen2: SecondScreen,
Screen3: ThirdScreen,
},
{
initialRouteName: "Tabs"
}
);
This works fine but when I try to use another StackNavigator for the Search Tab, two headers are displayed and the only solution I have found is to hide the nested StackNavigator's header with headerMode: "none":
const SearchStack = createStackNavigator(
{
Search: SearchScreen,
Post: PostScreen,
},
{
headerMode: "none",
navigationOptions: {
headerShown: false
},
initialRouteName: "Search"
}
);
However, this functionality is not best for my app. I need the outside StackNavigator's header to be hidden instead when the Search tab is in focus so the user can still go back and forth between screens in the nested Search Stack. Can someone advise me what I should do to achieve this?
I think you are using react-navigation in the wrong way, you should not make separate stack navigator for SearchStack, instead you should add SearchStack's screens in AppStack only.
const AppTabs = createBottomTabNavigator(
{
Home: Tab1,
Create: Tab2,
Search: Tab3,
Ask: Tab4
}
);
const AppStack = createStackNavigator(
{
Tabs: AppTabs,
Post: PostScreen,
Screen2: SecondScreen,
Screen3: ThirdScreen,
},
{
initialRouteName: "Tabs"
}
);
Change your AppStack navigator to the following:
const AppStack = createStackNavigator(
{
Tabs: AppTabs,
Screen2: SecondScreen,
Screen3: ThirdScreen,
},
{
defaultNavigationOptions: ({navigation}) => {
const currentRoute = navigation.state.routes[navigation.state.index];
const {routeName} = currentRoute;
let tabBarVisible = true;
if (routeName === 'Search') {
tabBarVisible = false;
}
return {
headerShown: tabBarVisible,
};
},
initialRouteName: 'Tabs',
},
);
If the app is rendering the Search route, the headerShown property of your top most StackNavigator (AppStack) will be set to false. Meaning the app will not show the AppStack header when rendering the Search route.
The SearchStack navigator can be defined simply like this:
const SearchStack = createStackNavigator(
{
Search: SearchScreen,
Post: PostScreen,
},
{
initialRouteName: 'Search',
},
);
Simply use below code in the page you want to hide the header
export default class SearchScreen extends Component {
static navigationOptions = {
header: null
}
}

React Navigation 4 MaterialTopTabNavigator - activeLabelStyle

I am creating a MaterialTopBar with React Navigation v4. I am facing an issue where i am not able to style to active state of the label - or even the tab.
The docs state that you can supply a function which renders a custom label. Here you could supply a custom style based on the focused parameter. This is what i am doing, but it seems like it just ignores my custom label function. Below is my code.
export default createMaterialTopTabNavigator(
{
Drinks: {
screen: PickupDrinksScreen,
},
Food: {
screen: PickupFoodScreen,
},
Snacks: {
screen: PickupSnacksScreen,
}
},
{
navigationOptions: ({ navigation }) => {
return {
tabBarLabel: ({ focused }) => {
const { routeName } = navigation.state;
return <Text style={[styles.label, focused && styles.focusedLabel]}>{routeName}</Text>;
}
};
}
})
Thanks in advance!
I managed to achieve this inside a BottomTabNavigator but I think that it does not really differ for your needs. Check out my code:
Secure: {
screen: SecureStack,
navigationOptions: {
tabBarIcon: ({ focused }) => {
if (focused) {
return <Ionicons name="md-unlock" size={24} color="#ccc" />;
} else {
return <Ionicons name="md-lock" size={24} color="#ccc" />;
}
},
title: "Secure"
}
}
try it like I did, I pass the navigationOptions right to the screen instead of passing it to all tabs. ~ Faded

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.

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

How to style TabNavigator's Tabs?

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',
},
}

Resources