ReactNative: navigationOptions not working - header

Not able to change the headertitle or headerstyle using the navigationOptions function. Also used defaultNavigationOptions instead but still not working.
const CategoriesScreen = props => {
const renderGridItem = itemData => {
return (
<TouchableOpacity
style={styles.gridItem}
onPress={() => {
props.navigation.navigate({ routeName: 'CategoryMeals' });
}}
>
<View>
<Text>{itemData.item.title}</Text>
</View>
</TouchableOpacity>
);
};
return (
<FlatList
keyExtractor={(item, index) => item.id}
data={CATEGORIES}
renderItem={renderGridItem}
numColumns={2}
/>
);
};
CategoriesScreen.navigationOptions = {
headerTitle: 'Meal Categories',
headerStyle: {
backgroundColor: Platform.OS === 'android' ? Colors.primaryColor : ''
},
headerTintColor: Platform.OS === 'android' ? 'white' : Colors.primaryColor
};
Any help would be appreciated. Thanks

You are going wrong as you should use the title instead of headerTitle like
title: 'Meal Categories',
instead of
headerTitle: 'Meal Categories',

Experienced The same Problem. In my case, I just had to reload my app

Related

How to pass a custom button in a drawer navigator, using the DrawerItemList in React Navigation v. 5?

I'm trying to migrate to react navigation 5, but I get an error where I use the DrawerItemList in place of the DrawerNavigatorItems.
My code used to look like this:
const MainNavigator = createDrawerNavigator(
// e.g.
Winners: {
screen: WinnersNavigator,
navigationOptions: {
drawerLabel: (
<BoldText style={NavigationStyles.arhiki}>Νικητές</BoldText>
),
drawerIcon: tabInfo => {
return (
<View style={NavigationStyles.winners}>
<FontAwesome
name="users"
size={iconMultiplier / 10}
color={tabInfo.tintColor}
/>
</View>
);
}
}
},
// ... more screens
contentComponent: props => {
const dispatch = useDispatch();
// This is for showing the Admin screen link, if user is an admin.
const userIdExists = useSelector(state => state.auth.userId);
return (
<View style={{ flex: 1 }}>
<SafeAreaView forceInset={{ top: "always", horizontal: "never" }}>
{/* These are the default drawer items */}
// THE PROBLEM IS HERE!!!
<DrawerNavigatorItems {...props} />
{/* Plus our custom buttons */}
{userIdExists && (
<View style={NavigationStyles.summary}>
<Ionicons.Button
name="ios-create"
backgroundColor={Colours.moccasin_light}
size={iconMultiplier / 10}
// style={{marginLeft: -20 }}
color="#888"
onPress={() => props.navigation.navigate("CreateWelcome")}
></Ionicons.Button>
<Text
onPress={() => props.navigation.navigate("CreateWelcome")}
style={[
NavigationStyles.exodos,
Platform.OS == "android" ? { marginLeft: -6 } : null
]}
>
Δημιουργία
</Text>
</View>
)}
...
Now it looks like this:
const CustomDrawerContent = props => {
const dispatch = useDispatch();
// This is for showing the Admin screen link, if user is an admin.
const userIdExists = useSelector(state => state.auth.userId);
return (
<View style={{ flex: 1 }}>
<SafeAreaView forceInset={{ top: "always", horizontal: "never" }}>
<DrawerContentScrollView {...props}>
{/* These are the default drawer items */}
<DrawerItemList {...props} />
{/* Plus our custom buttons */}
<Drawer.Section>
{userIdExists && (
<View style={NavigationStyles.summary}>
<DrawerItem
label={() => (
<Text
// onPress={() => props.navigation.navigate("CreateWelcome")}
style={[
NavigationStyles.exodos,
Platform.OS == "android" ? { marginLeft: -6 } : null
]}
>
Δημιουργία
</Text>
)}
icon={() => (
<Ionicons.Button
name="ios-create"
backgroundColor={Colours.moccasin_light}
size={iconMultiplier / 10}
// style={{marginLeft: -20 }}
color="#888"
// onPress={() => props.navigation.navigate("CreateWelcome")}
></Ionicons.Button>
)}
onPress={() => props.navigation.navigate("CreateWelcome")}
/>
</View>
)}
// ... more items
</Drawer.Section>
</DrawerContentScrollView>
</SafeAreaView>
</View>
);
};
const MainDrawerNavigator = createDrawerNavigator();
export const MainNavigator = () => {
return (
<MainDrawerNavigator.Navigator
drawerStyle={{
width: width < 900 ? 0.6 * width : 0.4 * width,
backgroundColor: Colours.moccasin_light,
overlayColor: Colours.maroonRGBA
}}
drawerContentOptions={{ activeTintColor: Colours.gr_brown }}
drawerContent={props => <CustomDrawerContent {...props} />}
>
<MainDrawerNavigator.Screen
name="GameNavigator"
component={GameNavigator}
options={{
drawerLabel: (
<BoldText style={NavigationStyles.arhiki}>Αρχική</BoldText>
),
drawerIcon: ({ color }) => (
<View style={NavigationStyles.shield}>
<MaterialCommunityIcons
name="shield-cross"
size={iconMultiplier / 8}
color={color}
/>
</View>
)
}}
/>
// ... more screens
</MainDrawerNavigator.Navigator>
);
};
The error I get is:
TypeError: label is not a function. (In 'label({
color: color,
focused: focused
})', 'label' is an instance of Object)
and it's generated at the <DrawerItemList {...props} />
In the docs I read:
import {
    DrawerContentScrollView,
    DrawerItemList,
  } from '#react-navigation/drawer';
  
  function CustomDrawerContent(props) {
    return (
      <DrawerContentScrollView {...props}>
        <DrawerItemList {...props} />
      </DrawerContentScrollView>
    );
  }
// To add additional items in the drawer, you can use the DrawerItem component:
function CustomDrawerContent(props) {
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<DrawerItem
label="Help"
onPress={() => Linking.openURL('https://mywebsite.com/help')}
/>
</DrawerContentScrollView>
);
}
The first screen that I render is a Navigator, the GameNavigator.
Could that be a problem?
I read in a issue that:
"It's not possible to add navigators inside drawer content. You can achieve custom layouts using a custom router and custom navigator:" source,but I've learned from a course that it is possible! Or does this guy mean something else with drawer content?
The GameNavigator is:
const GameStackNavigator = createStackNavigator();
const GameNavigator = () => {
return (
<GameStackNavigator.Navigator
initialRouteName="Welcome"
screenOptions={defaultNavOptions}
>
<GameStackNavigator.Screen
name="Welcome"
component={WelcomeScreen}
options={WelcomeScreenOptions}
/>
...
</GameStackNavigator.Navigator>
);
};
And the WelcomeScreen is:
const WelcomeScreen = props => {
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
const [gridTileAnimValue] = useState(new Animated.Value(0));
const [isConnected, setIsConnected] = useState(false);
// For adding the points that are saved in memory, when connection is established.
useEffect(() => {
const unsub = NetInfo.addEventListener(state => {
setIsConnected(state.isConnected);
});
return () => unsub();
}, []);
const getPoints = async () => {
let points = await AsyncStorage.getItem("savedPoints");
if (!!points) {
const getEmail = async () => {
const userData = await AsyncStorage.getItem("userData");
if (userData) {
const transformedData = JSON.parse(userData);
const { userEmail } = transformedData;
return userEmail;
}
};
const email = await getEmail();
// Give it some time to get the token and userId,
// because saveData needs them.
setTimeout(
async () => await dispatch(dataActions.saveData(email, +points)),
3000
);
await AsyncStorage.removeItem("savedPoints");
}
};
if (isConnected) getPoints();
useEffect(() => {
getFilters = async () => {
await dispatch(filtersActions.fetchDifficultyLevelFilters());
await dispatch(filtersActions.fetchCategoriesFilters());
};
}, [dispatch]);
useEffect(() => {
props.navigation.setOptions({
headerRight: () => (
<HeaderButtons HeaderButtonComponent={CustomHeaderButton}>
<Item
title="game-info"
iconName={
Platform.OS === "android"
? "md-information-circle-outline"
: "ios-information-circle-outline"
}
// style={{width: width / 8, height: height / 10, paddingTop: height / 35}}
onPress={() => setModalVisible(!modalVisible)}
/>
</HeaderButtons>
)
});
}, [modalVisible, setModalVisible]);
useEffect(() => {
const checkIfInfoNeeded = async () => {
return await AsyncStorage.getItem("NO_infoNeeded");
};
checkIfInfoNeeded().then(NO_infoNeeded => {
if (NO_infoNeeded === "NO") {
return;
} else {
setModalVisible(true);
}
});
}, []);
const animateGridTile = () => {
Animated.timing(gridTileAnimValue, {
toValue: 1,
duration: 1200,
useNativeDriver: false
}).start();
};
useEffect(() => {
animateGridTile();
const unsubscribe = props.navigation.addListener("focus", animateGridTile);
return () => unsubscribe();
}, [animateGridTile]);
useEffect(() => {
const unsubscribe = props.navigation.addListener("willBlur", () =>
gridTileAnimValue.setValue(0)
);
return () => {
unsubscribe();
};
}, []);
const cardStyle = { opacity: gridTileAnimValue };
const renderGridItem = itemData => {
return (
<Animated.View style={cardStyle}>
<CategoryGridTile
color={itemData.item.color}
title={itemData.item.title}
id={itemData.item.id}
onSelect={() => {
if (itemData.item.id == 0) {
props.navigation.navigate({
routeName: "MixedChoicesScreen"
});
} else if (itemData.item.id == 1) {
props.navigation.navigate({
routeName: "MultiChoiceCategories",
params: {
gameType: itemData.item.title
}
});
} else if (itemData.item.id == 2) {
props.navigation.navigate({
routeName: "TrueFalseCategories",
params: {
gameType: itemData.item.title
}
});
}
}}
/>
</Animated.View>
);
};
if (isLoading) {
return (
<CustomLinearGradient>
<View style={styles.centered}>
<ActivityIndicator size="large" color={Colours.moccasin_light} />
</View>
</CustomLinearGradient>
);
}
return (
<CustomLinearGradient>
<View style={styles.flatListContainer}>
{modalVisible && (
<CustomModal
modalVisible={modalVisible}
setModalVisible={setModalVisible}
onRequestClose={() => {
Alert.alert(
"Επιλογές",
"Παρακαλώ επιλέξτε μία από τις δύο επιλογές της καρτούλας: Ναι ή Όχι.",
[{ text: "Εντάξει", style: "default" }]
);
// Alert.alert("Παρακαλώ επιλέξτε μία από τις δύο επιλογές της καρτούλας: Πληροφορίες ή Δεν χρειάζεται.");
}}
textOne="Θέλετε να διαβάσετε τις οδηγίες χρήσεως και τις πληροφορίες σχετικά με τις
δοκιμαστικές εκδόσεις της εφαρμογής."
buttonOneTitle="Ναι"
buttonTwoTitle="Όχι"
onPressOne={async () => {
AsyncStorage.setItem("NO_infoNeeded", "NO");
setModalVisible(false);
props.navigation.navigate("GameInfo");
}}
onPressTwo={async () => {
AsyncStorage.setItem("NO_infoNeeded", "NO");
setModalVisible(false);
}}
/>
)}
<FlatList
// numColumns={2}
keyExtractor={(item, index) => item.id}
data={GAME_TYPES}
renderItem={renderGridItem}
/>
</View>
</CustomLinearGradient>
);
};
export const WelcomeScreenOptions = ({ route, navigation }) => {
return {
title: "ΕΝ ΤΟΥΤΩ ΝΙΚΑ",
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={CustomHeaderButton}>
<Item
onPress={() => navigation.toggleDrawer()}
title="Menu"
iconSize={73}
iconName={Platform.OS === "android" ? "md-menu" : "ios-menu"}
// style={{
// width: width / 8,
// height: height / 10,
// paddingTop: height / 35
// }}
/>
</HeaderButtons>
)
};
};
Any suggestion would be appreciated.
Thanks
For drawerLabel you have directly set an object which is wrong
drawerLabel: (
<BoldText style={NavigationStyles.arhiki}>Αρχική</BoldText>
),
This should be either a string or a function that returns a component, so you should change it like below
drawerLabel: {()=>(
<BoldText style={NavigationStyles.arhiki}>Αρχική</BoldText>
)},
You can refer the docs
https://reactnavigation.org/docs/drawer-navigator/#drawerlabel

Custom Tab Bar React Navigation 5

I'm trying to make a tab bar like in the picture bellow, using React Navigation.
I tried some codes, but nothing worked. Some codes are from previous React Navigation Version. The really problem is make the tab bar have a margin from from bottom and both sides, and borders rounded.
Anyone can help me please?!
here is demo: https://snack.expo.io/#nomi9995/createbottomtabnavigator-%7C-react-navigation
you can use tabBar props to make custom TABBAR
<NavigationContainer>
<Tab.Navigator tabBar={props => <MyTabBar {...props} />}>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
MyTabBar Component
function MyTabBar({ state, descriptors, navigation }) {
return (
<View style={{ flexDirection: 'row',backgroundColor:"#F4AF5F",height:50,borderRadius:50,justifyContent:"center",alignItems:"center" }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1, alignItems:"center" }}
>
<Text style={{ color: isFocused ? '#673ab7' : '#222' }}>
{label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
The key to this is to add the style position: 'absolute' to the outer <View> container on your custom TabBar. This will get rid of the white background issue.

Where is the `tabBarComponent` option in React-Navigation version 5?

I'm migrating a RN project version 4 to 5.
I was using the tabBarComponent option to replace tab bar component with a custom one. Old docs.
How to achieve the same in version 5, I can't find this option in the new docs.
Here is the new API way to achieve a custom tab component:
import { View, Text, TouchableOpacity } from 'react-native';
function MyTabBar({ state, descriptors, navigation }) {
return (
<View style={{ flexDirection: 'row' }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1 }}
>
<Text style={{ color: isFocused ? '#673ab7' : '#222' }}>
{label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
// ...
<Tab.Navigator tabBar={props => <MyTabBar {...props} />}>
{...}
</Tab.Navigator>
This link will surely help.
https://reactnavigation.org/docs/en/next/bottom-tab-navigator.html#tabbar
Hope this helps. Cheers!

How to add icons to tabs in react-native-tab-view

I am working with react-native-tab-view and I can just put text on tabs but I want icons. There are some answers on GitHub but they are unclear. If you know anything it would be perfect!
1. Get an imported icon library:-
import Icon from 'react-native-vector-icons/AwesomeFont'
2. Create a method for rendering the icons depending on route using props:-
const getTabBarIcon = (props) => {
const {route} = props
if (route.key === 'Search') {
return <Icon name='search' size={30} color={'red'}/>
} else {
return <Icon name='circle' size={30} color={'red'}/>
}
}
3. Render TabView with a rendered TabBar prop calling back to getTabBarIcon:-
export default class App extends React.Component {
state = {
index: 0,
routes: [
{key: 'Home', title: 'Hello'},
{key: 'Search', title: 'Second'}
],
}
render() {
return (
<TabView
navigationState={this.state}
renderScene={SceneMap({
Home: FirstRoute,
Search: SearchScreen,
})}
onIndexChange={index => this.setState({index})}
initialLayout={{height: 100, width: Dimensions.get('window').width}}
renderTabBar={props =>
<TabBar
{...props}
indicatorStyle={{backgroundColor: 'red'}}
renderIcon={
props => getTabBarIcon(props)
}
tabStyle={styles.bubble}
labelStyle={styles.noLabel}
/>
}
tabBarPosition={'bottom'}
/>
);
}
}
4. You can style the TabBar with anything (here the label is hidden to use icon only tabs)
const styles = StyleSheet.create({
scene: {
flex: 1,
},
noLabel: {
display: 'none',
height: 0
},
bubble: {
backgroundColor: 'lime',
paddingHorizontal: 18,
paddingVertical: 12,
borderRadius: 10
},
})
react-native
You have to override renderHeader method and define in TabBar your own render label method:
renderHeader = (props) => (
<TabBar
style={styles.tabBar}
{...props}
renderLabel={({ route, focused }) => (
<View style={styles.tabBarTitleContainer}>
/* HERE ADD IMAGE / ICON */
</View>
)}
renderIndicator={this.renderIndicator}
/>
);
I had the same problem. I solved it as follows by creating a "_renderTabBar" function and passing as props to the renderTabBar method of the TabView component and in this function I put the component "image" as my icon, I hope it helps.
A print:
_renderTabBar = props => {
const inputRange = props.navigationState.routes.map((x, i) => i);
return (
<View style={styles.tabBar}>
{props.navigationState.routes.map((route, i) => {
const color = props.position.interpolate({
inputRange,
outputRange: inputRange.map(
inputIndex => (inputIndex === i ? 'red' : 'cyan')
),
});
return (
<TouchableOpacity
style={[styles.tabItem, {backgroundColor: '#FFF' }]}
onPress={() => this.setState({ index: i })}>
<Image
style={styles.iconTab}
source={{uri: 'https://www.gstatic.com/images/branding/product/2x/google_plus_48dp.png'}}
/>
<Animated.Text style={{ color }}>{route.title}</Animated.Text>
</TouchableOpacity>
);
})}
</View>
);
};
Here you render
render() {
return (
<TabView
navigationState={this.state}
renderScene={this._renderScene}
renderTabBar={this._renderTabBar}
onIndexChange={index => this.setState({ index })}
/>
);
Code complete: https://snack.expo.io/#brunoaraujo/react-native-tab-view-custom-tabbar

Tabbed navigation between screens in react native

I use a StackNavigator based on the react-native-elements example and I want to enable something similar to a link with an ID as a parameter. I want to link to this screen:
const FontsTab = StackNavigator({
Home: {
screen: FontsTabView,
path: '/',
navigationOptions: ({ navigation }) => ({
title: 'Fonts',
headerLeft: (
<Icon
name="menu"
size={30}
type="entypo"
style={{ paddingLeft: 10 }}
onPress={() => navigation.navigate('DrawerOpen')}
/>
),
}),
},
Detail: {
screen: FontsDetailTabView,
path: 'fonts_detail',
navigationOptions: {
title: 'Fonts Detail',
},
},
});
I have this screen where I want the click of the text of an item to open the FontsTabView with the ID as a parameter. I would like to achieve something like the following:
<Text onPress={ (navigation)=> navigation.navigate('FontsTabView ', { id: {item.id} }) } style={styles.listHeader} >{item.title}</Text>
How can it be done?
class Icons extends Component {
constructor() {
super();
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
});
this.state = {
selectedIndex: 0,
value: 0.5,
dataSource: ds.cloneWithRows(list1),
isLoading: true
};
this.updateIndex = this.updateIndex.bind(this);
this.renderRow = this.renderRow.bind(this);
}
updateIndex(selectedIndex) {
this.setState({ selectedIndex });
}
renderRow(rowData, sectionID) {
return (
<ListItem
key={sectionID}
onPress={log}
title={rowData.title}
icon={{ name: rowData.icon }}
/>
);
}
_renderList = ({ item, navigation }) => {
return (
<TouchableWithoutFeedback onPress={(event) => this._selectedItem(item.key)}>
<View style={styles.listRowContainer}>
<View style={styles.listinside1Container}>
<Image style={styles.listImage} source={item.icon} />
<View style={styles.listContainer} onPress={(event) => this._selectedItem(item.text)} >
<Text onPress={ (navigation)=> navigation.navigate('DrawerOpen') } style={styles.listHeader} >{item.title}</Text>
<Text style={styles.listValue} >{item.value}</Text>
<Image
style={{width: 50, height: 50}}
source={{uri: item.img}}
/>
</View>
</View>
</View>
</TouchableWithoutFeedback>
);
}
componentDidMount(){
return fetch('https://www.koolbusiness.com/in.json')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson.movies,
}, function(){
});
})
.catch((error) =>{
console.error(error);
});
}
render() {
if(this.state.isLoading){
return(
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
const { navigation } = this.props;
const buttons = ['Button1', 'Button2'];
const { selectedIndex } = this.state;
if(this.state.isLoading){
return(
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return (
<ScrollView>
<View style={styles.headerContainer}>
<Icon color="white" name="invert-colors" size={62} />
<Text style={styles.heading}>Trending Ads India</Text>
</View>
<View style={styles.MainContainer}>
</View>
<View style={styles.mainWrapper} >
<FlatList data={this.state.dataSource} renderItem={this._renderList} keyExtractor={(item, index) => index.toString()} />
</View>
</ScrollView>
);
}
}
You were close but not quite there,
Try to render your item like below;
<Text onPress={ ()=> this.props.navigation.navigate('FontsTabView', { id: item.id }) } style={styles.listHeader} >
{item.title}
</Text>
Then in FontsTabView you can read the parameter like below and render your screen accordingly.
this.props.navigation.state.params.id