I'm using "react-navigation": "^2.11.2" and have a TabNavigator() with 3 tabs: A, B and C.
So I use:
...
_Profile: {
screen: TabNavigator(
{
First: A,
Second: B,
Third: C
},
{
tabBarPosition: "top",
swipeEnabled: true,
lazy: false
}
),
navigationOptions: ({ navigation }) => ({
header: <ProfileHeader navigation={navigation} />
})
},
...
I want to have a fixed footer in Pages A and B but NOT in C.
First I tried to create a footer in each A and B but the result is something different from what I want, See images below:
But when I try to swipe to tab B, You can see the footer is NOT FIXED:
Any idea about this?
Thanks in advance!
I asked the contributors and we have a full example from now on:
Custom Tabs with footer:
Github example
UPDATE
I guess the link is broken so I paste the code here:
import React from "react";
import {
LayoutAnimation,
View,
StyleSheet,
StatusBar,
Text
} from "react-native";
import { SafeAreaView, createMaterialTopTabNavigator } from "react-navigation";
import Ionicons from "react-native-vector-icons/Ionicons";
import { Button } from "./commonComponents/ButtonWithMargin";
class MyHomeScreen extends React.Component {
static navigationOptions = {
tabBarLabel: "Home",
tabBarIcon: ({ tintColor, focused, horizontal }) => (
<Ionicons
name={focused ? "ios-home" : "ios-home"}
size={horizontal ? 20 : 26}
style={{ color: tintColor }}
/>
)
};
render() {
const { navigation } = this.props;
return (
<SafeAreaView forceInset={{ horizontal: "always", top: "always" }}>
<Text>Home Screen</Text>
<Button
onPress={() => navigation.navigate("Home")}
title="Go to home tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
</SafeAreaView>
);
}
}
class RecommendedScreen extends React.Component {
static navigationOptions = {
tabBarLabel: "Recommended",
tabBarIcon: ({ tintColor, focused, horizontal }) => (
<Ionicons
name={focused ? "ios-people" : "ios-people"}
size={horizontal ? 20 : 26}
style={{ color: tintColor }}
/>
)
};
render() {
const { navigation } = this.props;
return (
<SafeAreaView forceInset={{ horizontal: "always", top: "always" }}>
<Text>Recommended Screen</Text>
<Button
onPress={() => navigation.navigate("Home")}
title="Go to home tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
</SafeAreaView>
);
}
}
class FeaturedScreen extends React.Component {
static navigationOptions = ({ navigation }) => ({
tabBarLabel: "Featured",
tabBarIcon: ({ tintColor, focused, horizontal }) => (
<Ionicons
name={focused ? "ios-star" : "ios-star"}
size={horizontal ? 20 : 26}
style={{ color: tintColor }}
/>
)
});
render() {
const { navigation } = this.props;
return (
<SafeAreaView forceInset={{ horizontal: "always", top: "always" }}>
<Text>Featured Screen</Text>
<Button
onPress={() => navigation.navigate("Home")}
title="Go to home tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
</SafeAreaView>
);
}
}
const SimpleTabs = createMaterialTopTabNavigator({
Home: MyHomeScreen,
Recommended: RecommendedScreen,
Featured: FeaturedScreen
});
class TabNavigator extends React.Component {
static router = SimpleTabs.router;
componentWillUpdate() {
LayoutAnimation.easeInEaseOut();
}
render() {
const { navigation } = this.props;
const { routes, index } = navigation.state;
const activeRoute = routes[index];
let bottom = null;
if (activeRoute.routeName !== "Home") {
bottom = (
<View style={{ height: 50, borderTopWidth: StyleSheet.hairlineWidth }}>
<Button title="Check out" onPress={() => {}} />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<StatusBar barStyle="default" />
<SafeAreaView
style={{ flex: 1 }}
forceInset={{ horizontal: "always", top: "always" }}
>
<SimpleTabs navigation={navigation} />
</SafeAreaView>
{bottom}
</View>
);
}
}
export default TabNavigator;
Related
Would anyone be able to give me help regarding React Navigation with Expo? The issue is with the drawer component where the 'Hamburger' icon isn't opening or closing the component when a user presses the icon. However, it does open/close on a default swipe gesture from React Navigation. Please see below for my code:
Router:
import React from 'react';
import { NavigationContainer, DrawerActions } from '#react-navigation/native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { IconButton } from 'react-native-paper'
import i18n from '../i18n/i18n';
//Theme
import Theme from '../theme/theme'
//Components
import Menu from '../components/Menu'
//Import Interfaces
import { RootDrawerParamList } from '../utils/typescript/type.d';
import { IProps } from '../utils/typescript/props.d';
//Import Screens
import Screen1 from '../screens/Screen1';
import Screen2 from '../screens/Screen2';
import SettingsScreen from '../screens/Settings';
const Drawer = createDrawerNavigator<RootDrawerParamList>();
export default class Router extends React.Component<IProps, any> {
constructor(props: IProps) {
super(props);
}
render() {
return (
<NavigationContainer>
<Drawer.Navigator
initialRouteName='Screen1'
drawerContent={(props: any) => <Menu {...props} />}
screenOptions={({
swipeEnabled: true
})}>
<Drawer.Screen
name="Screen1"
component={Screen1}
initialParams={{ i18n: i18n, Theme: Theme }}
options={({route, navigation} : any) => ({
headerRight: () => (<IconButton icon="cog" size={24} color={Theme.colors.text} onPress={() => navigation.navigate('Settings')} />),
route: {route},
navigation: {navigation}
})}
/>
<Drawer.Screen
name="Settings"
component={SettingsScreen}
initialParams={{ i18n: i18n, Theme: Theme }}
options={({route, navigation} : any) => ({
headerTitle: i18n.t('settings', 'Settings'),
headerLeft: () => (<IconButton icon="arrow-left" color={Theme.colors.text} size={24} onPress={() => navigation.goBack()} />),
route: {route},
navigation: {navigation}
})}
/>
<Drawer.Screen
name="Screen2"
component={Screen2}
initialParams={{ i18n: i18n, Theme: Theme }}
options={({route, navigation} : any) => ({
route: {route},
navigation: {navigation}
})}
/>
</Drawer.Navigator>
</NavigationContainer>
);
}
}
Menu
import React from 'react';
import { FlatList, StyleSheet, View } from 'react-native';
import { List, Title } from 'react-native-paper';
import { getDefaultHeaderHeight } from '#react-navigation/elements';
import { DrawerItem } from '#react-navigation/drawer';
import { useSafeAreaFrame, useSafeAreaInsets } from 'react-native-safe-area-context';
//Import Interfaces
import { IListItem } from '../utils/typescript/types.d';
import { IPropsMenu } from '../utils/typescript/props.d';
import { IStateMenu } from '../utils/typescript/state.d';
//A function is used to pass the header height, using hooks.
function withHeightHook(Component: any){
return function WrappedComponent(props: IPropsMenu) {
/*
Returns the frame of the nearest provider. This can be used as an alternative to the Dimensions module.
*/
const frame = useSafeAreaFrame();
/*
Returns the safe area insets of the nearest provider. This allows manipulating the inset values from JavaScript. Note that insets are not updated synchronously so it might cause a slight delay for example when rotating the screen.
*/
const insets = useSafeAreaInsets();
return <Component {...props} headerHeight={getDefaultHeaderHeight(frame, false, insets.top)} />
}
}
class Menu extends React.Component<IPropsMenu, IStateMenu> {
constructor(props: IPropsMenu) {
super(props);
this.state = {
menu: [
{
name: 'screen1.name',
fallbackName: 'Screen 1',
icon: 'dice-multiple-outline',
iconFocused: 'dice-multiple',
onPress: this.props.navigation.navigate.bind(this, 'screen1')
},
{
name: 'screen2.name',
fallbackName: 'Screen 2',
icon: 'drama-masks',
iconFocused: 'drama-masks',
onPress: this.props.navigation.navigate.bind(this, 'screen2')
}
]
}
}
renderItem = (item : IListItem) => {
const { i18n } = this.props.state.routes[0].params;
return (
<DrawerItem
label={ i18n.t(item.name, item.fallbackName) }
onPress={ item.onPress ? item.onPress: () => {} }
icon={ ({ focused, color, size }) => <List.Icon color={color} style={[styles.icon, {width: size, height: size }]} icon={(focused ? item.iconFocused : item.icon) || ''} /> }
/>
);
};
render() {
const { headerHeight } = this.props;
const { menu } = this.state;
const { Theme } = this.props.state.routes[0].params;
return (
<View>
<View style={{
backgroundColor: Theme.colors.primary,
height: headerHeight ?? 0,
}}>
<View style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-end',
}}>
<View style={{
flexDirection: 'row',
alignItems: 'center',
marginBottom: 5,
marginLeft: 5
}}>
<Title style={{ color: Theme.colors.text, marginLeft: 5 }}>
Title
</Title>
</View>
</View>
</View>
<FlatList
data={menu}
keyExtractor={item => item.name}
renderItem={({item}) => this.renderItem(item)}
/>
</View>
);
};
}
export default withHeightHook(Menu);
const styles = StyleSheet.create({
icon: {
alignSelf: 'center',
margin: 0,
padding: 0,
height:20
},
logo: {
width: 24,
height: 24,
marginHorizontal: 8,
alignSelf: 'center'
},
});
The solution to my issue was to encapsulate the drawer component in a native stack component. The 'Hamburger' icon works as expected, thanks for all the help and suggestions.
// Import Screens
import DrawRouter from './DrawRouter';
const Stack = createNativeStackNavigator<RootStackParamList>();
export default (): React.ReactElement => {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Main" component={DrawRouter} />
</Stack.Navigator>
</NavigationContainer>
);
};
Add the toogleDrawer() method to your onPress event on your Drawer.Navigator component:
onPress={()=> navigation.toggleDrawer()
I updated my react native application to SDK 42. After that left navigator bar is not working. it only popup and show image only. but if i comment contentComponent: CustomDrawerContentComponent part in the createDrawerNavigator then image disapear and my navigation menus are showing.
My Code
const CustomDrawerContentComponent = (props) => (
<ScrollView>
<Container style={styles.container}>
<Header style={styles.drawerHeader}>
<Body style={styles.headerBody}>
<Image
ref={(ref) => this.logoImgRef = ref}
style={{ width: 120, height: 120, marginTop:10}}
source={imgLogo}
/>
</Body>
</Header>
<Content>
<DrawerItems {...props}/>
</Content>
</Container>
</ScrollView>
)
const DrMenu= createDrawerNavigator({
"Home": {
navigationOptions: {
drawerIcon: () => (
<Icon name='home'
color='#1976D2' size={26}/>
),
title: i18n.t('home'),
},
screen: (props) => <FLHome {...props} propName={FLHome} {...contactData} />
},
"Logout": {
navigationOptions: {
drawerIcon: () => (
<Icon name='sign-out'
color='#1976D2' size={26}/>
),
title: i18n.t('logout'),
},
screen: (props) => <Logout {...props} propName={Logout} />
},
}, {
initialRouteName: "Home",
drawerPosition: 'left',
contentComponent: CustomDrawerContentComponent,
contentOptions: {
activeTintColor: '#1976D2',
inactiveTintColor :'#1999CE',
activeBackgroundColor :'#E8EAF6',
},
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
}
);
const App = createAppContainer(DrMenu);
return(
<App/>
);
}
react-native provides functional creation of rendering objects
const DateSelect = ()=>{
return (<DataSelect/>)
}
export default DateSelect
//import alias
import DateSelection from 'path';
//use
render.dom
<DateSelection/>
I found a solution for this problem. That is you have to create another JS page and render in this page like this below,
export default CustomDrawerContentComponent = props => (
<ScrollView>
<View style={styles.container}>
<Image
source={imgLogo}
style={{ width: 120, height: 120, marginTop:10}}
/>
</View>
<View>
<DrawerNavigatorItems {...props}/>
</View>
</ScrollView>
);
Then you can render in this way in the DrawerViewConfig
contentComponent: props => <CustomDrawerContentComponent {...props} />
I want to hide one of my tabs conditionally if user login,
So I have 5 Tabs If user login\register I get a boolean from a redux store,
if this user login i want to how a "Library tab" if not login, i don't want to show this tab "Library" with others and just keep 4 tabs in the App
Code
import {createAppContainer} from 'react-navigation';
import {createBottomTabNavigator} from 'react-navigation-tabs';
let {isLogin} = store.getState().user;
const TabHome = createBottomTabNavigator(
{
Home: {
screen: Home,
navigationOptions: {
tabBarLabel: 'Home',
},
},
Browse: {
screen: Browse,
navigationOptions: {
tabBarLabel: 'Browse',
},
},
Search: {
screen: Search,
navigationOptions: {
tabBarLabel: 'Search',
headerShown: false,
},
},
Radio: {
screen: Radio,
navigationOptions: {
tabBarLabel: 'Radio',
},
},
Library: isLogin ? (
{
screen: YourLibrary,
navigationOptions: {
tabBarLabel: 'Library',
},
}
) : (
<View /> // Or :null => Not work and got the under error msg
),
// Library: {
// screen: YourLibrary,
// },
},
)
export default createAppContainer(TabHome);
Error: The component for route 'Library' must be a React component.
For example:
import MyScreen from './MyScreen'; ... Library: MyScreen, }
You can also use a navigator:
import MyNavigator from './MyNavigator'; ... Library: MyNavigator, }
In React Navigation v5:
import * as React from 'react';
import { Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
function HomeScreen(props) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
</View>
);
}
function SettingsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
</View>
);
}
function AboutScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>About!</Text>
</View>
);
}
const Tab = createBottomTabNavigator();
function MyTabs() {
const [showTab, setShowTab] = React.useState(true);
// Show the about tab after 5 seconds.
// Change this to use Redux or however
// you would like to change which tabs are displayed
setTimeout(() => {
setShowTab(false);
console.log('Hide tab');
}, 5000);
return (
<Tab.Navigator>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
tabBarLabel: 'Home',
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="home" color={color} size={size} />
),
}}
/>
{showTab ? (
<Tab.Screen
name="About"
component={AboutScreen}
options={{
tabBarLabel: 'About',
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="book" color={color} size={size} />
),
}}
/>
) : null}
<Tab.Screen
name="Settings"
component={SettingsScreen}
options={{
tabBarLabel: 'Settings',
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="settings" color={color} size={size} />
),
}}
/>
</Tab.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<MyTabs />
</NavigationContainer>
);
}
Example in Expo https://snack.expo.io/#jackvial/createbottomtabnavigator-%7C-react-navigation-v5
You'd know that you can override the Tabbar Component and add your own logic for it?
Maybe this gives you an Idea about that: https://stackoverflow.com/a/58520533/1256697
Maybe this way, you can set conditional styles to show or hide single items of your TabBar.
remove Library tab definition from TabHome and add it just before exporting the component:
if(isLogin) {
TabHome.Library = {
screen: YourLibrary,
navigationOptions: {
tabBarLabel: 'Library',
}
}
}
export default createAppContainer(TabHome)
how is it possible to position the menu point at the lower edge of the screen?
The best solution would be if you could simply change the style of the navigation options for each element.
Here is my App Navigator:
const AppNavigator = createDrawerNavigator(
{
Einkaufsliste: {
screen: MainScreen,
navigationOptions: {
drawerIcon: <Icon name="shopping-cart" />
}
},
Bearbeiten: {
screen: BasketEditScreen,
navigationOptions: {
drawerIcon: <Icon name="edit" />
}
}
},
{
contentComponent: CustomDrawerComponent
}
);
Thanks!
According to react-navigation you can make a custom Drawer navigator, so what you need is to create a similar thing to their example:
import { DrawerItems, SafeAreaView } from 'react-navigation';
const CustomDrawerContentComponent = (props) => (
<ScrollView>
<SafeAreaView style={styles.container} forceInset={{ top: 'always', horizontal: 'never' }}>
<DrawerItems {...props} />
</SafeAreaView>
</ScrollView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
So in your case what you want to do is:
import { DrawerItems, SafeAreaView } from 'react-navigation';
const CustomDrawerContentComponent = (props) => (
<ScrollView>
<SafeAreaView style={styles.container} forceInset={{ top: 'always', horizontal: 'never' }}>
<View style={{flex: 1 }}>
<DrawerItems {...props} />
</View>
<TouchableOpacity style={{flexDirection: 'row', alignItems: 'center'}}>
//Your pencil icon here with correct margin to the right
<Text>Bearbeiten</Text>
</TouchableOpacity>
</SafeAreaView>
</ScrollView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
I want to show a hamburger icon on the left side(it should be shown on all the header who uses drawer navigation) and post property button on the right side. I tried using headerRight and headerLeft but it is not showing any icons or buttons or whatever.
Here is what i have done
const SimpleStack = StackNavigator({
Home: {
screen: MyHomeScreen
},
PostProperty: {
screen: PostProperty
}
});
class DrawerView extends React.Component {
render() {
const { navigation } = this.props;
return (
<View>
<View style={{ backgroundColor: "red", padding: 100 }} />
<View style={{ padding: 20 }}>
<TouchableOpacity onPress={() => navigation.navigate("Rent")}>
<Text>Rent</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate("Buy")}>
<Text>Buy</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate("PostProperty")}>
<Text>Post Property</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const DrawerStack = DrawerNavigator(
{
Main: {
screen: SimpleStack
},
Rent: {
screen: Rent
},
Buy: {
screen: Buy
}
},
{
contentComponent: DrawerView,
drawerWidth: 280
}
);
export default DrawerStack;
const MyHomeScreen = ({ navigation }) => (
<View style={styles.container}>
<Text>Home</Text>
</View>
);
MyHomeScreen.navigationOptions = {
title: "RoomFinder",
drawer: {
icon: () => {
<Image source={require("../../assets/menu#2x.png")} />;
},
headerRight: <Button title="Post Property" />
}
};
const Rent = ({ navigation }) => (
<View>
<ScrollView horizontal={true} showsHorizontalScrollIndicator={true}>
<View>
<Text>
Rent Screen
</Text>
</View>
</ScrollView>
</View>
);
Rent.navigationOptions = {
title: "Rent"
};