How can TabNavigator child screen refer to parent StackNavigator? - react-native

The structure of the application is this.
StackNavigator
StackA-Screen(TabNavigator)
TabChildA-Screen
TabChildB-Screen
...
StackB-Screen
code in App.js
const TabComponent = TabNavigator(
{
TabChildA-Screen: {
screen: TabChildA,
},
TabChildB-Screen: {
screen: TabChildB,
}
},
{
tabBarPosition: "bottom",
animationEnabled: false,
swipeEnabled: false
}
);
const StackComponent = StackNavigator(
{
StackA-Screen: {
screen: TabComponent,
navigationOptions: ({ navigation }) => ({
headerBackTitle: null,
headerRight: (
<TouchableWithoutFeedback
onPress={() =>
navigation.navigate("StackB-Screen", { space: "" }) // can't navigate to "StackB-Screen".
}
>
<MaterialIcon
name="playlist-add"
size={Sizes.NavigationBar.Icon.Size}
style={{ padding: 8, color: Colors.White }}
/>
</TouchableWithoutFeedback>
)
})
},
StackB-Screen: {
screen: StackB,
}
},
{
initialRouteName: "StackA-Screen",
mode: "modal"
}
);
export default StackComponent;
I want to navigate TabChildA-Screen to StackB-Screen.
But, TabChildA-Screen can refer to navigator is navigator of TabNavigator.
code in TabChildA-Screen
import React, { Component } from "react";
import { Button, Text, View } from "react-native";
class StackB extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center"
}}
>
<Button
onPress={ () =>
this.props.navigation.navigate("StackB-Screen") // can't navigate to "StackB-Screen".
}
title="move to StackB-Screen"
/>
</View>
);
}
}
export default StackB;
How to TabChildA-Screenrefer StackNavigator ?

Related

react-navigation go Back not navigate to previous screen

I'm using new react-navigation 2.16.0 in my react-native app.
I have welcome (login and register) screen and then Main stack. main stack include all screens after success auth(login\register).
MainStack = createStackNavigator(
{
Welcome: {
screen: Welcome, navigationOptions: {
gesturesEnabled: false,
}
},
Main: {
screen: App, navigationOptions: {
gesturesEnabled: false,
}
}
}, {
headerMode: 'none',
lazy: true,
initialRouteName: UserStore.token ? 'Main' : 'Welcome',
gesturesEnabled: false,
cardStack: {
gesturesEnabled: false
},
cardStyle: {
elevation: 0,
shadowOpacity: 0,
borderBottomWidth: 0,
borderTopWidth: 0
},
transitionConfig: () => ({
containerStyle: {
elevation: 0,
shadowOpacity: 0,
borderBottomWidth: 0,
borderTopWidth: 0
}
}),
}
)`
Main stack render
render() {
// const splashDone = this.state.splashDone && this.state.backSplash
const ready = UserStore.storeHydrated
console.log('Current Routes', NavigationStore)
// console.log('AppStore.loading ', AppStore.loading)
return (
<View style={{ flex: 1,backgroundColor:'transparent'}}>
<StatusBar
barStyle="light-content"
/>
{!splashDone ? <SplashScreen /> : null}
{ready &&
<Provider {...stores}>
<MainStack
/>
</Provider>}
<InternetConnectionPopUp />
{AppStore.loading ?
<Spinner
color={colors.yellow}
style={{ position: 'absolute', right: 0, left: 0, top: 0, bottom: 0, zIndex: 99999 }} />
: null}
<View style={Platform.OS === 'ios' && this.state.flag ? { height: calcSize(25) } : {}} />
</View>
)
}
App.js
import React, { Component } from 'react'
import { BackHandler, Alert, AppState, View,Keyboard } from 'react-native'
import { inject, observer } from 'mobx-react/native'
import { AppStack } from '../../routes'
import { NavigationActions } from 'react-navigation'
import { Header } from '../../components';
let popupOpen = false
#inject('AppStore') #observer
class App extends Component {
constructor(props) {
super(props)
this.state = {
appState: AppState.currentState,
nowMounted: false
}
this.goBack = this.goBack.bind(this)
}
goBack() {
this.props.navigation.goBack(null)
}
componentWillMount() {
this.setState({ nowMounted: true })
}
render() {
return (
<View style={{ flex: 1 }}>
<Header onPressBack={this.goBack}/>
<AppStack/>
</View>
)
}
}
export default App
AppStack.js
import {
Dashboard,
Services,
Schedule,
ScheduleDays,
ScheduleTime,
CancelAppointment
} from '../screens'
import { createStackNavigator, NavigationActions } from 'react-navigation'
export const AppStack = createStackNavigator({
Dashboard: { screen: Dashboard, navigationOptions: {
gesturesEnabled: false,
} },
Services: { screen: Services, navigationOptions: {
gesturesEnabled: false,
} },
Schedule: { screen: Schedule, navigationOptions: {
gesturesEnabled: false,
} },
ScheduleDays: { screen: ScheduleDays, navigationOptions: {
gesturesEnabled: false,
} },
ScheduleTime: { screen: ScheduleTime, navigationOptions: {
gesturesEnabled: false,
} },
CancelAppointment: { screen: CancelAppointment, navigationOptions: {
gesturesEnabled: false,
} },
}, {
headerMode: 'none',
initialRouteName: 'Dashboard',
lazy: true,
gesturesEnabled: false,
cardStack: {
gesturesEnabled: false
},
})
goBack not works in createStackNavigator, it stay in same screen.
go Back not works at all.
when I navigate from dashboard to services screen and then press onBack in services it do nothing.
I also tried to change instead of createStackNavigator to createSwitchNavigator but still it not works.
Could you please put the code of the service screen where you call the goBack function, it could be helpful.
Generally you just call
this.props.navigation.goBack()
or
this.props.navigation.goBack(null)
you can also try
this.props.navigation.navigate('Dashboard')
if it's in the history of the stack it will go back to the previous screen from service screen instead of pushing it on top
This work for me.
<Button
title="go back"
onPress={(props) => { this.props.navigation.goBack(null) }}
/>
Dont forget to remove this for functional component.
Functional Component Approach
import {StyleSheet,Text,View,TouchableOpacity,} from 'react-native';
const ScreenName = ({ navigation }) => {
return (
<View style={styles.container}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Text>
GoBack
</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container:{
flex:1,
display: 'flex',
justifyContent:'center',
alignItems: 'center',
}
});
export default ScreenName;
Navigation prop has a goBack helper.
class HomeScreen extends React.Component {
render() {
const {goBack} = this.props.navigation;
return (
<View>
<Text>This is the home screen of the app</Text>
<Button
onPress={() => goBack()}
title="Go to Brent's profile"
/>
</View>
)
}
}

React Navigation: A drawer with a stack and you want to hide the drawer on certain screens, but it doesn't hide

I'm facing the situation described in the docs, where I have a drawer with a stack and I want to hide the drawer on certain screens. Unfortunately the code below, influenced by the docs, does not work and the drawer can still be opened on pushed stack screens.
const MenuStack = createStackNavigator(
{
CheckedInMenu: { screen: MenuScreen },
CheckedIdMenuItemDetail: { screen: MenuItemDetailScreen }
},
{
navigationOptions: ({ navigation }) => {
let options = {
headerTitleStyle: {
color: headerColor
},
headerBackTitleStyle: {
color: headerColor
},
headerTintColor: headerColor
};
let drawerLockMode = "unlocked";
if (navigation.state.index > 0) {
drawerLockMode = "locked-closed";
}
return { ...options, drawerLockMode };
}
}
);
const checkedInDrawer = createDrawerNavigator(
{
MenuStack: {
screen: MenuStack,
navigationOptions: {
drawerLabel: SCREEN_TEXT_MENU_HEADER,
drawerIcon: ({ tintColor }) => (
<Image
source={require("../assets/icons/menu.png")}
resizeMode="contain"
style={{ width: 25, height: 25, tintColor: tintColor }}
/>
)
}
}
},
{
initialRouteName: "MenuStack",
drawerBackgroundColor: backgroundColor,
contentComponent: BurgerMenu,
contentOptions: {
activeTintColor: activeTintColor,
inactiveTintColor: headerColor,
activeBackgroundColor: backgroundColor,
itemStyle: { borderBottomWidth: 1, borderColor: borderColor },
labelStyle: { fontSize: 16, fontWeight: "500" }
}
}
);
What am I doing wrong?
Edit
Even if I console.log() the everything like this:
let options = {
headerTitleStyle: {
color: headerColor
},
headerBackTitleStyle: {
color: headerColor
},
headerTintColor: headerColor
};
let drawerLockMode = "unlocked";
console.log(navigation);
if (navigation.state.routeName !== "CheckedInMenu") {
drawerLockMode = "locked-closed";
}
if (navigation.state) console.log(navigation.state.routeName);
console.log({ ...options, drawerLockMode: drawerLockMode });
return { ...options, drawerLockMode: drawerLockMode };
It says on the CheckedInMenuItemDetailScreen that drawerLockMode = "locked-closed".
EDIT 2:
I now found out that the ONLY way to achieve this is exactly the way the docs say. You must do it like this:
MainStack.navigationOptions = ({ navigation }) => {
let drawerLockMode = "unlocked";
if (navigation.state.index > 0) {
drawerLockMode = "locked-closed";
}
return {
drawerLockMode
};
};
And you must try to do it within the navigationOptions of the definition of the stack, like I did in my original post above. Keep that in mind!
This code works. When navigate to DetailsScreen, the DrawerMenu is hidden. I have implemented it using your referenced the offical docs here.
import React, { Component } from 'react';
import { Platform, StyleSheet, TouchableHighlight, Text, View } from 'react-native';
import { createStackNavigator, createDrawerNavigator, createSwitchNavigator } from 'react-navigation';
class ProfileScreen extends Component {
render() {
return (
<View>
<Text> ProfileScreen </Text>
</View>
)
}
}
class DetailsScreen extends Component {
render() {
return (
<View>
<Text> DetailsScreen </Text>
</View>
)
}
}
class HomeScreen extends Component {
render() {
const { navigate } = this.props.navigation
return (
<View>
<Text> HomeScreen </Text>
<TouchableHighlight
onPress={() => navigate("Details", { screen: "DetailsScreen" })}
>
<Text>Screen One </Text>
</TouchableHighlight>
</View>
)
}
}
const FeedStack = createStackNavigator({
FeedHome: {
screen: HomeScreen,
navigationOptions: {
title: "test"
}
},
Details: DetailsScreen,
});
FeedStack.navigationOptions = ({ navigation }) => {
let drawerLockMode = 'unlocked';
if (navigation.state.index > 0) {
drawerLockMode = 'locked-closed';
}
return {
drawerLockMode,
};
};
const DrawerNavigator = createDrawerNavigator({
Home: FeedStack,
Profile: ProfileScreen,
});
const AppNavigator = createSwitchNavigator(
{
Drawer: DrawerNavigator,
}
);
export default class App extends Component {
render() {
return (
<View style={{ flex: 1 }} >
<AppNavigator />
</View>
);
}
}

how can i change the options of my drawerNavigator with React Native when user is logged

I need to change the content of my DrawerNavigator once the user is logged.
Can someone help me, please?
You can make your custom DrawerNavigator and change its View or Content dynamically. Here is an example:
public static Routes = DrawerNavigator({
Main: {
screen: StackNavigator({
Home: { screen: Home },
Contents: { screen: Contents },
ContentList: { screen: ContentList },
}, stackConfig('Home'))
},
MessageInbox: { screen: MessageInbox },
UserInfo: { screen: UserInfo}
}, {
initialRouteName: 'Main',
drawerWidth: 300,
drawerPosition: Platform.OS == 'ios' ? 'left' : 'right',
contentComponent: (props: any) => (
<DrawerComponent properties={props} />
)
}
)
DrawerComponent.js:
import React from 'react'
import { View, Platform, Text, Image, ScrollView, TouchableOpacity, AsyncStorage } from 'react-native'
import { DrawerItems, NavigationActions } from 'react-navigation'
import Icon from 'react-native-vector-icons/Ionicons';
export default class DrawerComponent extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
user: null,
avatarPic: 'avatars/0-1.png',
}
}
componentDidMount() {
this.fetchData()
}
fetchData = async () => {
let data = await AsyncStorage.getItem('UserData')
this.setState({ user: data })
}
render() {
let { user } = this.state
return (
<ScrollView contentContainerStyle={{ flex: 1 }}>
<View style={{ width: '100%', height: '30%', backgroundColor: 'white' }}>
<Image
style={{ width: 50, height: 50 }}
source={{ uri: this.state.avatarPic }}
/>
<TouchableOpacity
onPress={() => {
this.props.properties.navigation.navigate('UserInfo')
}}
style={{ marginTop: 10 }}>
<Text>{user.name ? user.name : 'New User'}</Text>
</TouchableOpacity>
</View>
<DrawerItems {...this.props.properties} />
</ScrollView>
)
}
}
I hope it help you.

How to navigate between screens from any js class that is not inside App.js in React Native

It's very easy to navigate from one screen to another that is inside App.js class. What I have done is made three classes : App.js, SearchList.js and Detail.js. But i am facing issue that how to navigate from searchList.js to Detail.js on click any view inside searchList.js class. Should i use StackNavigator again in searchList.js or declare all classes in App.js ?
App.js
import React from 'react';
import { Image,Button, View, Text ,StatusBar,StyleSheet,Platform,TouchableOpacity,ImageBackground,Picker,Alert,TouchableHighlight} from 'react-native';
import { StackNavigator,DrawerNavigator,DrawerItems } from 'react-navigation';
import {Constants} from "expo";
import SearchList from './classes/SearchList';
import Detail from './classes/Detail';
const DrawerContent = (props) => (
<View>
<View
style={{
backgroundColor: '#f50057',
height: 160,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text style={{ color: 'white', fontSize: 30 }}>
Header
</Text>
</View>
<DrawerItems {...props} />
</View>
)
class HomeScreen extends React.Component {
static navigationOptions = {
drawerLabel: 'Home',
drawerIcon: ({ tintColor }) => (
<Image
source={require('./images/crown.png')}
style={[styles.icon, {tintColor: '#f50057'}]}
/>
),
};
constructor(){
super();
this.state={PickerValueHolder : ''}
}
GetSelectedPickerItem=()=>{
Alert.alert(this.state.PickerValueHolder);
}
render() {
return (
<ImageBackground source={require('./images/green.png')} style={styles.backgroundImage} >
<TouchableOpacity onPress={() =>this.props.navigation.navigate('DrawerOpen')}>
<Image
source={require('./images/menu-button.png')}
style={styles.imagesStyle}
/>
</TouchableOpacity>
<View style={styles.columnContainer}>
<TouchableHighlight style={styles.search} underlayColor='#fff' onPress={() => this.props.navigation.navigate('SearchList')}>
<Text style={styles.searchText}>Search Hotels</Text>
</TouchableHighlight>
</View>
</ImageBackground >
);
}
}
class DetailsScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button
title="Go to Details... again"
onPress={() => this.props.navigation.navigate('Details')}
/>
</View>
);
}
}
const styles = StyleSheet.create({
backgroundImage: {
flex: 1,
width: null,
height: null,
marginTop: Constants.statusBarHeight,
},
search:{
marginTop:20,
paddingTop:15,
borderRadius:8,
borderColor: '#fff'
},
searchText:{
color:'#fff',
textAlign:'center',
}
// backgroundColor: '#ef473a', // app color
});
const HomeStack = StackNavigator({
Home: {
screen: HomeScreen,
navigationOptions: ({ navigation }) => ({
header: null,
})
},
SearchList: { screen: SearchList },
Detail: { screen: Detail},
});
const RootStack = DrawerNavigator(
{
Home: {
screen: HomeStack,
},
DetailsScreen: {
screen: DetailsScreen,
},
},
{
initialRouteName: 'Home',
}
);
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
SearchList.js
import React, { Component } from 'react';
import { StyleSheet, Platform, View, ActivityIndicator, FlatList, Text, Image, Alert, YellowBox,ImageBackground } from 'react-native';
import { StackNavigator,} from 'react-navigation';
import Detail from './classes/Detail';
export default class SearchList extends Component {
constructor(props) {
super(props);
this.state = {isLoading: true}
YellowBox.ignoreWarnings([
'Warning: componentWillMount is deprecated',
'Warning: componentWillReceiveProps is deprecated',
]);
}
GetItem (flower_name) {
Alert.alert(flower_name);
}
FlatListItemSeparator = () => {
return (
<View
style={{
height: .0,
width: "100%",
backgroundColor: "#000",
}}
/>
);
}
webCall=()=>{
return fetch('https://reactnativecode.000webhostapp.com/FlowersList.php')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
}, function() {
// In this block you can do something with new state.
});
})
.catch((error) => {
console.error(error);
});
}
componentDidMount(){
this.webCall();
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<View style={styles.MainContainer}>
<FlatList
data={ this.state.dataSource }
ItemSeparatorComponent = {this.FlatListItemSeparator}
renderItem={({item}) =>
<ImageBackground source= {{ uri: item.flower_image_url }} style={styles.imageView}
onPress={() => this.props.navigation.navigate('Detail')}>
</ImageBackground>
}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer :{
justifyContent: 'center',
flex:1,
margin: 5,
marginTop: Constants.statusBarHeight , //(Platform.OS === 'ios') ? 20 : 14,
},
imageView: {
width: '100%',
height: 220 ,
margin: 7,
borderRadius : 40,
},
});
const HomeStack = StackNavigator({
Detail: { screen: Detail},
});
export default class App extends React.Component {
render() {
return <HomeStack />;
}
}
Any help would be appreciable.
To navigate to any screen you need to have a navigation object. Navigation object can be provided in two ways
By declaring it in StackNavigator
By explicitly passing it as a prop to some other screen
If you use the first approach, and you need to navigate from SecondScreen to ThirdScreen, both of your screens should be declared in the StackNavigator first, only then navigation will be successful.
If you are using any trivial component ( such as a modal box ) to navigate to another screen, all you need to do is pass the navigation props (this.props.navigation) to the modal box component and use the props to navigate to another screen. The only requirement here being, this.props.navigation should be available in the class where the modal box component is loaded.
EDIT
As requested, here is the snippet
const App = StackNavigator({
FirstScreen: { screen: FirstScreen},
SecondScreen: { screen: SecondScreen},
ThirdScreen: { screen: ThirdScreen}
})
export default App;
In your SecondScreen, declare an object const { navigate } = this.props.navigation; and on a button click, use this object to navigate to another screen navigate("ThirdScreen");
Regarding the second approach, if your component is a modal, you can pass the navigate object as - <Modal navigation={navigate} /> and in the modal component you can use it as this.props.navigation("ThirdScreen");
Hope it clarifies now.
Support we have js named SecondScreen.js at the same directory level as App.js then we should import it like this in App.js
import SecondScreen from './SecondScreen';
It worked for me. Hope this helps to you too.
I think you are trying to implement the functionality of a stack navigator.
Go to React-Navigation-Docs. In stack navigator you can make stack of screens, and navigate from one to another. Inside index.js :
import { StackNavigator, TabNavigator } from "react-navigation";
import SplashScreen from "./src/screens/start/splash";
import LoginScreen from "./src/screens/start/login";
import DomainScreen from "./src/screens/start/domain";
const App = StackNavigator(
{
Splash: {
screen: SplashScreen,
},
Domain: {
screen: DomainScreen,
},
Login: {
screen: LoginScreen,
},
Tabs: {
screen: HomeTabs,
}
},
{
initialRouteName: "Splash",
}
);
AppRegistry.registerComponent("app_name", () => App);
then you can navigate to any of these screens using this.props.navigation.navigate("ScreenName")

Header with gradient image overlay

I am trying to add a header with a gradient image overlay to an app I am making.
The below code has been trimmed down and simplified in the hope to make it meaningful to you.
The header should be visible on all screens, where some screens show the back button and only the front screen show a logo and settings to the right.
How can I solve that?
import { TabNavigator, StackNavigator } from 'react-navigation';
import React, { Component } from 'react';
import Example from '../components/example';
const navContainer = (Comp, options) => {
return StackNavigator({
Main: {
screen: Comp,
navigationOptions: options
},
S1: {
screen: Example
},
S2: {
screen: Example,
navigationOptions: ({ navigation }) => {
return {
headerTitle: <Example {...navigation.state.params} />,
headerStyle: {
backgroundColor: 'white'
}
}
}
},
S3: {
screen: Example,
navigationOptions: ({ navigation }) => {
return {
headerTitle: <Example {...navigation.state.params} />,
headerStyle: {
backgroundColor: 'white'
}
}
}
},
S4: {
screen: Example,
navigationOptions: ({ navigation }) => {
return {
headerTitle: <Example {...navigation.state.params} />,
headerStyle: {
backgroundColor: 'white'
}
}
}
}
},
{
cardStyle: {
backgroundColor: 'green'
}
})
}
const navOptions = title => {
return {
headerTitle: title,
headerBackTitle: null,
headerStyle: {
backgroundColor: 'transparent'
}
}
}
const NavTab = TabNavigator(
{
M1: {
screen: navContainer(Example, navigation => ({
headerTitle: <Example />,
headerRight: <Example { ...navigation } />,
headerStyle: {
backgroundColor: 'transparent'
}
}))
},
M2: {
screen: navContainer(Example, navOptions('M2'))
},
M3: {
screen: navContainer(Example, navOptions('M3'))
},
M4: {
screen: navContainer(Example, navOptions('M4'))
}
},
{
tabBarPosition: 'bottom',
lazy: true,
tabBarOptions: {
inactiveBackgroundColor: 'white',
activeBackgroundColor: 'white'
}
}
);
export default NavTab;
The example component:
import React, { Component } from 'react';
import { StyleSheet, View, Text } from 'react-native';
export default class Example extends Component {
static style = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
render () {
return (
<View style={ Example.style.container }>
<Text>hello</Text>
</View>
);
}
}
My first attempt was to overwrite Header._renderHeader, but the status bar was still not covered.
The final solution is below the <hr />.
import React from 'react';
import { Header } from 'react-navigation';
import { StyleSheet, View, StatusBar, Image } from 'react-native';
import img_gradient from '../images/headerbg.png';
const style = StyleSheet.create({
header: {
flexDirection: 'row'
},
img: {
width: '100%',
height: '100%',
resizeMode: 'stretch'
}
});
export default class NavHeader extends Header {
_renderHeader (props) {
const left = this._renderLeft(props);
const right = this._renderRight(props);
const title = this._renderTitle(props, {
hasLeftComponent: left !== null,
hasRightComponent: right !== null
});
return (
<View key={ `scene_${ props.scene.key }` } style={ [StyleSheet.absoluteFill, style.header] }>
<StatusBar barStyle="light-content" />
<Image source={ img_gradient } style={ style.img }>
{ title }
{ left }
{ right }
</Image>
</View>
);
}
}
Here's how I solved it, by putting my root element inside BgImg: <BgImg><App /></BgImg>.
import React, { Component } from 'react';
import { StyleSheet, Image, StatusBar } from 'react-native';
import img_gradient from '../images/headerbg.png';
export default class BgImg extends Component {
static style = StyleSheet.create({
img: {
width: '100%',
height: '100%',
resizeMode: 'stretch'
}
})
render () {
return (
<Image source={ img_gradient } style={ BgImg.style.img }>
<StatusBar barStyle="light-content" />
{ this.props.children }
</Image>
);
}
}