I'm on ReactNative and i'm using native-base and react-navigation npm.
I got this and my question is how I can have a header, up to the button "Home", I looked into the documentation of react-navigation but it's not really cleared.
https://github.com/react-community/react-navigation/blob/master/docs/api/navigators/DrawerNavigator.md
Like this (the image is fix, it's just to put a logo here)
You can implement custom content component for drawer. There you can also simply render navigation items using DrawerItems. For example:
import React from 'react'
import { Text, View } from 'react-native'
import { DrawerItems, DrawerNavigation } from 'react-navigation'
const DrawerContent = (props) => (
<View>
<View
style={{
backgroundColor: '#f50057',
height: 140,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text style={{ color: 'white', fontSize: 30 }}>
Header
</Text>
</View>
<DrawerItems {...props} />
</View>
)
const Navigation = DrawerNavigator({
// ... your screens
}, {
// define customComponent here
contentComponent: DrawerContent,
})
For Drawer Navigation, you Can add Your own Header & Footer and Make Your Own Styles with contentComponent Config:
First import { DrawerItems, DrawerNavigation } from 'react-navigation' Then
Header Before DrawerItems:
contentComponent: props => <ScrollView><Text>Your Own Header Area Before</Text><DrawerItems {...props} /></ScrollView> .
Footer After DrawerItems:
contentComponent: props => <ScrollView><DrawerItems {...props} /><Text>Your Own Footer Area After</Text></ScrollView> .
You can achieve it using contentComponent option in drawer nav config. Two ways you could do it based on the level of configuration required:
Method 1.
DrawerItems from react-navigation(handles navigation on its own) -
import {DrawerItems, DrawerNavigation} from 'react-navigation';
export default DrawerNavigator({
// ... your screens
}, {
// define customComponent here
contentComponent: (props) =>
<View style={{flex: 1}}>
<Text>Header</Text>
<ScrollView>
<DrawerItems {...props} />
</ScrollView>
</View>
});
This creates a fixed header with scroll view for the menu items below it.
Method 2.
Creating your own custom component -
import { DrawerNavigation } from 'react-navigation'
export default DrawerNavigator({
// ... your screens
}, {
// define customComponent here
contentComponent: props => <SideMenu {...props}>
});
Here SideMenu is your own component to display in the drawer. You can use react-navigation NavigationActions.navigate(screen) to handle routing on onPress of menu items.
For more detailed explanation on the second method https://medium.com/#kakul.gupta009/custom-drawer-using-react-navigation-80abbab489f7
nested navigator should be like this:
const Router = StackNavigator({
Home: {screen: HomeScreen},
Test: {screen: TestScreen}
}, {
navigationOptions: {
headerStyle: {backgroundColor: '#2980b9'},
headerTintColor: '#fff'
}
});
const Drawer = DrawerNavigator({
App: {screen: Router}
});
for ui:
1) https://github.com/GeekyAnts/native-base-react-navigation-stack-navigator/blob/master/src/SideBar/SideBar.js
2) https://github.com/GeekyAnts/native-base-react-navigation-stack-navigator/blob/master/src/HomeScreen/index.js
Related
I'm starting an app and I have two views at the moment, one called Splash and the other called Home. It happens that when the splash is over it leads me to the Home view, but in this view the user presses the button backwards, the app shows me the splash again. Is there a way to avoid this? the idea is that being in the Home view there is no way to roll back the application.
MainStackNavigator.js
import * as React from 'react'
import { NavigationContainer } from '#react-navigation/native'
import { createStackNavigator } from '#react-navigation/stack'
import Splash from '../views/Splash/Splash';
import Home from '../views/Home/Home';
const Stack = createStackNavigator()
function MainStackNavigator() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name='Splash' component={Splash} options={{ headerShown: false}} />
<Stack.Screen name='Home' component={Home} />
</Stack.Navigator>
</NavigationContainer>
)
}
export default MainStackNavigator
Splash.js
import React, { Component } from 'react'
import { View, ImageBackground, Image } from 'react-native'
// import SplashContext from '../../state/splashContext'
var bg = require('../../../assets/img/bg.png');
var logo = require('../../../assets/img/logo.png')
export default class Splash extends Component {
constructor(props) {
super(props);
setTimeout(() => {
this.props.navigation.navigate("Home");
}, 500)
}
render() {
return (
<ImageBackground
source={bg}
style={{ height: '100%', width: '100%' }}>
<View
style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Image source={logo}
style={{ height: 100, width: 100 }}
>
</Image>
</View>
</ImageBackground>
);
}
}
Home.js
import React, { Component } from 'react'
import { View, Text } from 'react-native'
export default class Splash extends Component {
render() {
return (
<View
style={{ flex: 1, padding: 10, alignItems: 'center', justifyContent: 'center' }}
>
<Text
style={{ fontSize: 30 }}
>Homse</Text>
</View>
);
}
}
App.js
import React from 'react';
import MainStackNavigator from './src/navigation/MainStackNavigator'
const App: () => React$Node = () => {
return (
<MainStackNavigator></MainStackNavigator>
);
};
export default App;
Using "navigate" will go to the next page adding it to the stack navigator. Instead you want to replace the current page (splash screen) with the home page. This can be done using
the replace function
this.props.navigation.replace("Home");
See https://reactnavigation.org/docs/navigation-prop/
You could use a modal to show the splash on the same screen while the information is loading instead of having two different views. Have a "loading" variable in the state of the view initialized to "true". This variable will be the "visibility" boolean for your modal. After everything loads, change the "loading" variable to "false".
Here's an example with "useState" hook:
const [isLoading, setIsLoading] = useState(true);
const loadInfo = async () => {
/*do your stuff*/
/*after stuff's done*/
setIsLoading(false);
};
if (isLoading) {
return (
<MySplashScreenModal/>
);
} else {
return (
<MyHomeScreen/>
);
}
The main reason for using the modal is because you can cover the entire screen with it, including the status bar.
I used this plugin for my android and ios app for the splash screen and it works great and smooth. I recommend everyone
React native bootsplash for splash screen
I am a beginner to React Native and have encountered a problem with navigation. I want my App.js screen to be used for navigation, but I want another file to be the home screen. Is there any way to do this?
This is my App.js code:
import {createAppContainer} from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
import {HomeScreen} from './components/HomeScreen';
const MainNavigator = createStackNavigator({
Home: {screen: HomeScreen},
Login: {screen: LoginScreen},
});
const App = createAppContainer(MainNavigator);
export default App;
And this is the code of the page I want to make my home screen:
import React from 'react';
import {SafeAreaView, StyleSheet, ScrollView, View, Text, StatusBar, Image, TouchableOpacity} from 'react-native';
import {Colors} from 'react-native/Libraries/NewAppScreen';
class HomeScreen extends Component {
render() {
const {navigate} = this.props.navigation;
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.scrollView}>
<Image style={styles.logo} source={require('./components/images/img1.jpg')} />
<Image style={styles.logo} source={require('./components/images/img2.jpg')} />
<Image style={styles.logo} source={require('./components/images/img3.jpg')} />
<Image style={styles.logo} source={require('./components/images/img4.jpg')} />
<Image style={styles.logo} source={require('./components/images/img7.jpg')} />
<Image style={styles.logo} source={require('./components/images/img8.jpg')} />
</ScrollView>
</SafeAreaView>
</>
);
};
}
Thanks.
To make any screen initial you should set it as initialRouteName option like:
const RootStack = createStackNavigator(
{
Home: HomeScreen,
Login: LoginScreen,
}, {
initialRouteName: "Home"
}
);
To be able to import HomeScreen (or any other) from another file you need to export it from that file first. That part is missing from your example.
import * as React from 'react';
import { Button, View, Text } from 'react-native';
export default class HomeScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => this.props.navigation.navigate('Details')}
/>
</View>
);
}
}
Here is full snack example for your case, it is extended from react-navigation basic example, do check their documentation.
I have main Tabs "Categories" and I want when I click to any one of them it will just appeared his Details,
so I'm decleare a flag to every category and update it when clicked to display category details, BUT I think this is a wrong idea! and other issue when I click to first category it's appear his details, but when I clicked other Category from the tabs, the first category it's still around!
So how can I handle these issue?
Snack Here
Here's my Code
import React, { Component } from 'react';
import { Text, View, ScrollView,TouchableOpacity } from 'react-native';
export default class App extends Component {
state = {
open:true,
cat2:false,
cat3:false,
cat4:false,
}
render() {
return (
<View style={{flex: 1}} >
<ScrollView style={{flexGrow: 0.05, backgroundColor: '#347ed8', paddingTop: 50}} horizontal>
<TouchableOpacity onPress={()=>this.setState({open:!this.state.open})} style={{width: 100}}><Text style={{color:"#fff",fontSize:18}}>cat1 </Text></TouchableOpacity>
<TouchableOpacity
onPress={()=>this.setState({
cat2:!this.state.cat2
})}
style={{width: 100}}><Text style={{color:"#fff",fontSize:18}}>Cat2</Text></TouchableOpacity>
<TouchableOpacity style={{width: 100}}><Text style={{color:"#fff",fontSize:18}}>Cat3</Text></TouchableOpacity>
<TouchableOpacity style={{width: 100}}><Text style={{color:"#fff",fontSize:18}}>Cat4</Text></TouchableOpacity>
<TouchableOpacity style={{width: 100}}><Text style={{color:"#fff",fontSize:18}}>Cat5</Text></TouchableOpacity>
</ScrollView>
<View style={{flex: 0.95, backgroundColor: '#ddd'}}>
{this.state.open && <View>
<Text>Category Details One Here</Text>
</View>}
{this.state.cat2 && <View>
<Text>Category Details Two Here!</Text>
</View>}
{this.state.cat3 && <View>
<Text>Category Details Three Here!</Text>
</View>}
{this.state.cat4 && <View>
<Text>Category Details four Here!</Text>
</View>}
</View>
</View>
);
}
}
A simple solution to do what you want is to use a React-navigation module.
This problem should be that you have not set up touch events elsewhere, and the values shown by setting the conditions for touch events are different. However, this condition can mess up your code, which can be handled simply using the 'React-navigation' module.
You can use createMaterialTopTabNavigator
Example
import {
createStackNavigator,
createMaterialTopTabNavigator,//React navigation version 4 and before
createAppContainer,
} from 'react-navigation';
import { createMaterialTopTabNavigator } from 'react-navigation-tabs'; //React navigation version 4
//import Navigator in our project
import FirstPage from './pages/FirstPage';
import SecondPage from './pages/SecondPage';
//Making TabNavigator which will be called in App StackNavigator
//we can directly export the TabNavigator also but header will not be visible
//as header comes only when we put anything into StackNavigator and then export
const TabScreen = createMaterialTopTabNavigator(
{
Home: { screen: FirstPage },
Settings: { screen: SecondPage },
},
{
tabBarPosition: 'top',
swipeEnabled: true,
animationEnabled: true,
tabBarOptions: {
activeTintColor: '#FFFFFF',
inactiveTintColor: '#F8F8F8',
style: {
backgroundColor: '#633689',
},
labelStyle: {
textAlign: 'center',
},
indicatorStyle: {
borderBottomColor: '#87B56A',
borderBottomWidth: 2,
},
},
}
);
//making a StackNavigator to export as default
const App = createStackNavigator({
TabScreen: {
screen: TabScreen,
navigationOptions: {
headerStyle: {
backgroundColor: '#633689',
},
headerTintColor: '#FFFFFF',
title: 'TabExample',
},
},
});
I am trying to add a simple customized navigation menu to my react-native app, but the issue that I am coming across right now is that I can't seem to find a way to navigate to the selected menu items corresponding screen. I tried the normal this.props.navigation.navigate('Home'), but it seems that there is no navigation prop, which makes sense because in my app I am assuming that the prop for navigation is passed down to the screens from my app.js through the use of <AppContainer />.
I have tried using the MainNavigator object in my App.js but it doesn't seem to be working and doesn't have a navigate function or anything like that.
I have also tried changing the structure of my render function in App.js a little bit but it still does not seem to be having much of an effect.
This is my App.js
import React, { Component } from 'react';
import {
View,
Text,
TouchableOpacity
} from 'react-native';
import {
createStackNavigator,
createAppContainer } from 'react-navigation';
// SCREEN
import MainScreen from './screens/MainScreen';
import CostAnalysis from './screens/CostAnalysis';
import DriverLog from './screens/DriverLog';
// SIDE MENU
import SideMenu from 'react-native-side-menu';
// REDUX IMPORTS
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { recordReducer } from './reducers/recordReducer';
import { databaseReducer } from './reducers/databaseReducer';
const MainNavigator = createStackNavigator({
Home: {screen: MainScreen},
DriverLog: {screen: DriverLog},
CostAnalysis: {screen: CostAnalysis},
}, {
defaultNavigationOptions: {
header: null
}
});
const AppContainer = createAppContainer(MainNavigator);
const rootReducer = combineReducers(
{records: recordReducer,
database: databaseReducer});
const store = createStore(rootReducer);
class App extends Component {
render() {
const menu = (<View style={{
backgroundColor: '#f0f0f0',
alignContent: 'center',
textAlign: 'center', height: '100%', width: '100%', paddingTop: '40%'}}>
<Text style={styles.menuTitle}>{'S K I P\nD R I V E R\nL O G'}</Text>
<TouchableOpacity onPress={() => {this.props.navigation.navigate('Home')}}>
<Text style={styles.menuItem}>HOME</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => {MainNavigator.navigate('DriverLog')}}>
<Text style={styles.menuItem}>DRIVING LOG</Text>
</TouchableOpacity>
</View>);
return (
<SideMenu menu={menu} >
<Provider store={store}>
<AppContainer />
</Provider>
</SideMenu>
);
}
}
const styles = {
menuTitle: {
marginBottom: 60,
fontSize: 40,
textAlign: 'center',
color: '#e74c3c'
},
menuItem: {
marginBottom: 10,
fontSize: 26,
textAlign: 'center'
}
}
export default (App);
Ideally I don't have to re-structure my entire app as I have made a lot of progress in other areas, but I would really like the menu to simply link to the correct places.
I'm still very new to react-native so I really don't know what else to try. If anyone can give me a hand it would be greatly appreciated!
Thanks :)
PS: See a picture of the menu to illustrate what I mean
Menu Screenshot
you have to implement the constructor
class App extends Component {
constructor(props) {
super(props);
}
}
or
if you are using a drawer use it as a separate component
import React, { Component } from 'react';
import {
View,
Text,
TouchableOpacity
} from 'react-native';
class SideDrawer extends Component{
constructor(props) {
super(props);
}
render(){
return(
<View style={{
backgroundColor: '#f0f0f0',
alignContent: 'center',
textAlign: 'center', height: '100%', width: '100%', paddingTop: '40%'}}>
<Text style={styles.menuTitle}>{'S K I P\nD R I V E R\nL O G'}</Text>
<TouchableOpacity onPress={() => {this.props.navigation.navigate('Home')}}>
<Text style={styles.menuItem}>HOME</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => {MainNavigator.navigate('DriverLog')}}>
<Text style={styles.menuItem}>DRIVING LOG</Text>
</TouchableOpacity>
</View>
)
}
}
export default sideDrawer
then in your navigator
const HomeNavigator = StackNavigator(
Home: {screen: MainScreen},
DriverLog: {screen: DriverLog},
CostAnalysis: {screen: CostAnalysis},
}
const MainNavigator = DrawerNavigator({
Home: {
screen: HomeNavigator,
},
}, {
contentComponent: sideDrawer,
drawerWidth: width * .7,
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
});
I'm having trouble calling react navigation methods from custom components outside of my original screens, specifically the one I'm working on right now is trying to call goBack() in a back arrow of a custom header component I made (code below). The error message I'm getting when I click the back arrow is:
undefined is not an object (evaluating '_this2.props.navigation.goBack')
Here is the code:
// HeaderText.js
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Platform } from 'react-native';
import { Icon } from 'expo';
export class HeaderText extends React.Component {
render() {
const needsBackButton = this.props.backIcon;
if (needsBackButton) {
return(
<View style={[styles.headerStyle,styles.buttonHeaderStyle]}>
<TouchableOpacity onPress={() => this.props.navigation.goBack()} style={styles.backButtonStyles}><Icon.Ionicons size={25} style={{ color: '#fff', fontWeight: 'bold' }} name={Platform.OS === 'ios' ? `ios-arrow-back` : 'md-arrow-back'} /></TouchableOpacity>
<Text style={styles.textStyle}>{this.props.headerText}</Text>
<View style={styles.emptyViewStyles} />
</View>
);
} else {
return(
<View style={styles.headerStyle}>
<Text style={styles.textStyle}>{this.props.headerText}</Text>
</View>
);
}
}
}
Here is the screen I'm putting that HeaderText component in:
// SubmitReferralScreen.js
import React from 'react';
import {
Image,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
ImageBackground
} from 'react-native';
import { MonoText } from '../../components/general/StyledText';
import { HeaderText } from '../../components/general/HeaderText';
import { HomeScreenContainer } from '../../components/homeScreen/HomeScreenContainer';
import { IconButton } from '../../components/general/IconButton';
import { StatusInfo } from '../../constants/StatusInfo';
import SvgUri from 'react-native-svg-uri';
export default class SubmitReferralScreen extends React.Component {
static navigationOptions = {
header: null,
};
render() {
return (
<View style={{flex: 1, width: '100%',justifyContent: 'center', alignItems: 'center'}}>
<ImageBackground source={require('../../assets/images/background.png')} style={{width: '100%', height: '100%', flex: 1, justifyContent: 'flex-start', alignItems: 'center', backgroundColor: 'background-color: rgba(0, 0, 0, 0.5)',}}>
<HeaderText backIcon='true' headerText='New Referral' />
<Text>Submit referral here!</Text>
</ImageBackground>
</View>
);
}
}
And here is my Stack Navigator for the referral Screens:
// MainTabNavigator.js
const ReferralStack = createStackNavigator({
Referrals: ReferralScreen,
MakeReferral: SubmitReferralScreen,
});
I've looked at this StackOverflow answer: Getting undefined is not an object evaluating _this.props.navigation
And the answer there was to put only navigation.navigate(YourScreen). I tried that, and the error I got said "cannot find variable navigation".
How can I call navigation props from custom react native components?
By default only screen components are provided with the navigation prop. You can either use library provided ways of hooking up arbitrary components to the navigation state, or you can pass navigation as a prop manually.
Option #1. Using withNavigation:
React navigation exports a higher-order component through which you can inject the navigation props into any component you want. To do this, you can do something like:
Don't immediately export the HeaderText component class (remove export from that line)
At the bottom of that file add export default withNavigation( HeaderText );
or if you don't want to use a default export and keep it as a named export, instead do:
const NavigationConnected = withNavigation( HeaderText );
export { NavigationConnected as HeaderText };
Option #2. Passing navigation as prop: In your SubmitReferralScreen component you can simply pass this.props.navigation as a prop to the HeaderText component like: <HeaderText navigation={ this.props.navigation } />
It's because your navigation prop didn't found where is the navigation's value prop from the parent. Better you make HeaderText component using regular arrow function, like this;
const HeaderText = ({ needsBackButton }) => {
if(needsBackButton) {
return (
<View style={[styles.headerStyle,styles.buttonHeaderStyle]}>
<TouchableOpacity onPress={() => this.props.navigation.goBack()} style={styles.backButtonStyles}><Icon.Ionicons size={25} style={{ color: '#fff', fontWeight: 'bold' }} name={Platform.OS === 'ios' ? `ios-arrow-back` : 'md-arrow-back'} /></TouchableOpacity>
<Text style={styles.textStyle}>{this.props.headerText}</Text>
<View style={styles.emptyViewStyles} />
</View>
)
}
return (
// another component
)
}
And then, You can simply use useNavigation() to access navigation prop from any screen/component.
First, import useNavigation on component that handled function of the moving screen.
import { useNavigation } from '#react-navigation/native';
Create some constant to reference this module:
const navigation = useNavigation()
And then, simply use this on your TouchableOpacity's onPress prop like this;
<TouchableOpacity
onPress={() => navigation.goBack()}
style={styles.backButtonStyles}
>
//...
</ TouchableOpacity>
Get the complete documentation on this:
https://reactnavigation.org/docs/connecting-navigation-prop/