I am new to react-native and i am trying to add drawer to my app.
I am sharing my code with you.
export default class Dashboard extends React.Component {
constructor(props){
super(props);
this.state = {
}
}
closeControlPanel = () => {
this._drawer.close()
};
openControlPanel = () => {
this._drawer.open()
};
const drawerStyles = {
drawer: { shadowColor: '#000000', shadowOpacity: 0.8,backgroundColor:'' shadowRadius: 3},
main: {paddingLeft: 3},
}
render(){
let drawerContent = [];
drawerContent.push(
<View style={{ backgroundColor:'red', textAlign:'center' }}>
<Text style={{ color:'#ffffff', fontSize:17, alignSelf:'center' }}>Hiiiiiiiiiii</Text>
</View>
);
<Drawer
type="overlay"
tapToClose={true}
content = {drawerContent}
openDrawerOffset={0.2} // 20% gap on the right side of drawer
panCloseMask={0.2}
closedDrawerOffset={-3}
styles={drawerStyles}
tweenHandler={(ratio) => ({
main: { opacity:(2-ratio)/2 }
})}
>
</Drawer>
}
}
can anyone tell me where should be content of drawer and how to write open and close drawer functionality.Thank you...
you have not given the ref property on Drawer
Try adding this .
<Drawer
ref={c => this._drawer = c} // <==== Add this line
type="overlay"
tapToClose={true}
content = {drawerContent}
openDrawerOffset={0.2} // 20% gap on the right side of drawer
panCloseMask={0.2}
closedDrawerOffset={-3}
styles={drawerStyles}
tweenHandler={(ratio) => ({
main: { opacity:(2-ratio)/2 }
})}
>
</Drawer>
Related
I am beginner in React Native. I am getting stuck in one issue. I have one Parent component Home.js in which there is Tab navigator on click of tab 3 child components replace based on selected key. In same page, I have custom drawer.Now, I want to change tabs on click of custom drawer's option & same thing when my 1st tab selected 1st option of drawer also set selected. How can i achieve this.
Here is my navigation Drawer :
export default MyDrawerNavigator = DrawerNavigator({
Page1: {
screen: props => <Home {...props} />,
}
},
{
contentComponent: props => (<CustomSideMenu {...props} />),
drawerWidth: (getScreenWidth() * 2.5) / 3,
}
);
Here is my Home class I want to access goToNextTab() inside Custom drawer
export class Home extends React.Component {
static navigationOptions = hidenavigation;
constructor(props) {
super(props);
}
apply_header = (val) => {
this.props.navigation.setParams({ Title: val });
}
goToNextTab = (tabName) => {
this.setState({ activeTab: tabName });
}
openDrawer() {
this.props.navigation.openDrawer();
}
tabs = [{
key: 'Dashboard',
icon: 'speedometer',
label: 'Dashboard',
pressColor: 'rgba(255, 255, 255, 0.16)'
},
{
key: 'Add Diamond',
icon: 'plus-circle-outline',
label: 'Add Diamond',
pressColor: 'rgba(255, 255, 255, 0.16)'
},
{
key: 'Diamond',
icon: 'diamond-stone',
label: 'Diamond',
pressColor: 'rgba(255, 255, 255, 0.16)'
}]
state = {
activeTab: 'Dashboard',
showFooter: true
};
renderIcon = icon => ({ isActive }) => (
<Icon size={24} color={isActive ? COLOR.action_bar : COLOR.tab_deselected_text_color} name={icon} />
)
renderTab = ({ tab, isActive }) => (
<FullTab isActive={isActive} key={tab.key} label={tab.label} labelStyle={isActive ? style.activeText : style.deactiveText} renderIcon={this.renderIcon(tab.icon)} />
)
render() {
const propsForChild = {
goToNextTab: (tabName) => this.goToNextTab(tabName),
openDrawer: () => this.openDrawer()
};
const propsForNav = {
nav: this.props,
openDrawer: () => this.openDrawer()
};
const addDimPropsForChild = {
openDrawer: () => this.openDrawer()
}
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
{
this.state.activeTab === 'Add Diamond' ? <Add_Dimond_Stack screenProps={addDimPropsForChild} /> : this.state.activeTab === 'Diamond' ? <Dimond_List_stack screenProps={propsForNav} /> : <Dashboard_Stack screenProps={propsForChild} />
}
</View>
{
this.state.showFooter ?
<BottomNavigation activeTab={this.state.activeTab} renderTab={this.renderTab} tabs={this.tabs} onTabPress={newTab => { this.setState({ activeTab: newTab.key }); }} />
: null
}
</View>
);
}
componentWillMount() {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow.bind(this));
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide.bind(this));
}
componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow() {
//alert('Keyboard Shown');
this.setState({ showFooter: false })
}
_keyboardDidHide() {
//alert('Keyboard Hidden');
this.setState({ showFooter: true })
}
componentDidMount() {
printLogs('call', 'componentDidMount')
const { setParams } = this.props.navigation;
setParams({ myProps: 'test' });
}
}
Here is my Custom Drawer in which i want to access setSelectedPos() from Home tab click
export default class Custom_Side_Menu extends React.Component {
static navigationOptions = { hidenavigation };
state = {
current_selected: 0
}
setSelectedPos(pos) {
this.setState({ current_selected: pos });
}
closeNavigationPanel(pos) {
if (pos != 3) {
this.props.navigation.closeDrawer();
}
}
redirectToProfile() {
new NavigationRedirection().goToNextScreen('profile', this.props);
}
selectedColor(pos) {
if (this.state.current_selected === pos) {
return COLOR.input_text_color;
} else {
return COLOR.input_hint_color;
}
}
render() {
return (
<ScrollView>
<View style={stylePage.bg}>
{/* */}
<View style={{ flex: 1 }}>
<View style={{ padding: 10, alignContent: 'center', flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity onPress={() => { this.closeNavigationPanel() }}>
<Icon name="arrow-left" size={30} color={COLOR.input_text_color} />
</TouchableOpacity>
<Text style={stylePage.menu_title}>Menu</Text>
</View>
<TouchableWithoutFeedback onPress={() => {
this.redirectToProfile();
}}>
<View>
<Image style={stylePage.profileImage} source={{ uri: 'https://uinames.com/api/photos/female/22.jpg' }} />
<Text style={stylePage.name}>Ruth McCoy</Text>
<Text style={stylePage.email}>ruth.mccoy#example.com</Text>
</View>
</TouchableWithoutFeedback>
<View style={stylePage.line_seprator} />
<View style={stylePage.menu_options}>
<Text style={[stylePage.menu_text, { color: this.selectedColor(0) }]} onPress={() => this.setCurrentSelection(0)}>Dashboard</Text>
<Text style={[stylePage.menu_text, { color: this.selectedColor(1) }]} onPress={() => this.setCurrentSelection(1)}>Diamonds List</Text>
<Text style={[stylePage.menu_text, { color: this.selectedColor(2) }]} onPress={() => this.setCurrentSelection(2)}>Add diamonds</Text>
<Text style={[stylePage.menu_text, { color: this.selectedColor(3) }]} onPress={() => this.setCurrentSelection(3)}>Profile</Text>
<Text style={[stylePage.menu_text, { color: this.selectedColor(4) }]} onPress={() => this.setCurrentSelection(4)}>Change Password</Text>
</View>
</View>
<TouchableOpacity style={{ alignSelf: 'baseline' }} onPress={() => clearAllData(this.props)}>
<View style={stylePage.logout_btn}>
<IconAnt name="logout" size={25} color={COLOR.white} />
<Text style={stylePage.logout_title}>Logout</Text>
</View>
</TouchableOpacity>
<RBSheet
closeOnDragDown={true}
closeOnPressMask={false}
ref={ref => { this.RBSheet = ref }}
height={getScreenHeight() / 2} duration={250} customStyles={{
container: { padding: 10, borderTopLeftRadius: 20, borderTopRightRadius: 20 },
}}>
<ChangePassword {...this.props} RBSheet={this.RBSheet} />
</RBSheet>
</View>
</ScrollView>
);
}
setCurrentSelection(pos) {
this.closeNavigationPanel(pos);
this.setSelectedPos(pos);
if (pos === 3) {
this.redirectToProfile();
} else if (pos === 4) {
this.RBSheet.open();
} else {
printLogs('props', this.props.navigation)
}
}
}
There are two problems.
Click on drawer options to change the navigation tab
On tab change set the option as active
using redux as global store
There is an easy way out if you need redux as your global store.
first connect your components with react-redux connect
manage the activeTab state in store instead of component state
then on click of drawer option change the state in redux for your activetab
this way you are able to solve the problem 1
Also make sure you check activetab from store if matched you can update the styling for active option in drawer . So here is solution for problem 2
Using tab navigator from react-navigation
another option is using tabNavigator from react-navigation itself that way you only need to call navigator function for changing tab
and getting the active tab from navigation state
* alternate to redux *
you can use react context apis for managing your parent state if you are not using redux
If I set the navigationOptions in the screen component inside a StackNavigator to change the header of the stack inside a DrawerNavigator the navigationOptions are not taken into account. I can however pass defaultNavigationOptions which are taken into account. If I don't pass defaultNavigationOptions it doesn't change the behavior. This is with react-navigation v 4.x
here I create the Stack navigators screens for my Drawer
drawerNavigator = () => {
let drawerRoutes = {}
this.state.routes.forEach(r => {
let stackRoute = {}
let t = () => <First name={r} />
stackRoute[r] = { screen : t }
let stackOptions = {
initialRouteName: r,
defaultNavigationOptions: stackNavigationOptions
}
let s = createStackNavigator(stackRoute, stackOptions)
drawerRoutes[r] = { screen : s }
})
let drawerOptions = {
drawerWidth: '75%',
initialRouteName: this.state.routes[0],
contentComponent: props => <Menu {...props} />,
contentOptions: drawerItemsOptions
}
return createDrawerNavigator(drawerRoutes, drawerOptions);
}
Then in my component I try to set the navigationOptions of the Stack but it is not taken into account...
export default class First extends Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: 'hahahaha',
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
}
}
render() {
return (
<SafeAreaView style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<StatusBar transluscent backgroundColor="transparent" barStyle="dark-content" />
<Text>{this.props.name}</Text>
</SafeAreaView>
);
}
}
Okay I've discovered that if I pass my screen that way
stackRoute[r] = { screen : First }
instead of the
let t = () => <First name={r} />
stackRoute[r] = { screen : t }
the navigationOptions are taken into account. However I cannot pass props that way....
I have this compononet of mine ... where i want to draw a logout button and also hide the back arrow . but i am not able to do so . Can anyone tell me where i am doing it wrong? I have followed the original documentation of react navigation as well but no solution.
class Welcome extends React.Component {
constructor(props) {
super(props)
this.state = {
user: this.props.navigation.state.params.user,
}
}
static navigationOptions = ({ navigation}) => {
const { params = {} } = navigation ;
return {
headerTitle : "Welcome",
headerLeft: null,
headerRight : (
<TouchableOpacity
style={{ backgroundColor: '#29434e' , padding: 10}}
onPress={() => params.onlogout}
>
<Text style={{ marginVertical:5, color: 'rgba(255,255,255,0.7)', fontSize: 20,}}> Logout </Text>
</TouchableOpacity>
)
};
};
_Logout = () => {
this.props.Signout();
};
componentDidMount() {
this.props.navigation.setParams({ onlogout : this._Logout , isSaving: false})
}
}
}
const mapDispatchToProps = (dispatch) => {
return {
Signout: () => dispatch(Signout())
}
}
export default connect(null,mapDispatchToProps)(Welcome)
That is because of the marginVertical:10 on your Text style. Remove it and you should see your button.
Here is a working example: https://snack.expo.io/rJOSqqEHS
So the problem is component is not getting the header options using static navigationOptions thing but when i try the defaultNavigationOptions it is working perfect and the code is as belows :
const otherApp = createStackNavigator({
Welcome : {
screen : WelcomeScreen
}
},
{
defaultNavigationOptions : ({navigation}) => ({
title : 'Welcome',
headerStyle: {
backgroundColor: '#29434e',
shadowColor: 'transparent',
elevation: 0
},
headerRight: (
<TouchableOpacity
style={{ backgroundColor: '#DDDDDD', padding: 5 }}
onPress={() => navigation.getParam('logout')}>
<Text
style={{
fontSize: 10,
}}>
Logout
</Text>
</TouchableOpacity>
),
})
});
Hope it will help someone in future.
My environment is :
"react": "16.8.6",
"react-native": "0.60.3",
"react-navigation": "^3.11.0",
I want to reload the tabNavigator when the user changse the tab each time. the lifecycle method of react native doesn't get called when user changes the tab. Then how can it be possible to reload tab in TabNavigator :
The below example have two Tabs : 1) Home 2)Events. Now I want to refresh the event Tab when user returns from the home tab.
EXPO LINK : Expo Tab Navigator
Code :
import React, {Component} from 'react';
import {View, StyleSheet, Image, FlatList, ScrollView, Dimensions } from 'react-native';
import { Button, List, ListItem, Card } from 'react-native-elements' // 0.15.0
//import { Constants } from 'expo';
import { TabNavigator, StackNavigator } from 'react-navigation'; // 1.0.0-beta.11
//image screen width and height defs
const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;
export default class App extends Component {
render() {
//const { navigate } = this.props.navigation;
return (
<TabsNav />
)
}
}
class MyHomeScreen extends Component {
render() {
return (
<View>
<Image
resizeMode="cover"
style={{ width: windowWidth * .85, height: windowHeight * 0.3}}
source={{uri: 'http://www.ajaxlibrary.ca/sites/default/files/media/logo.png?s358127d1501607090'}}
/>
<Button
onPress={() => this.props.navigation.navigate('Notifications')}
title="Go to notifications"
/>
</View>
);
}
}
class AplEvents extends Component {
static navigationOptions = {
tabBarLabel: 'Events List',
tabBarIcon: ({ tintColor }) => (
<Image
source={{uri: 'https://facebook.github.io/react/img/logo_og.png'}}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
};
constructor(props) {
super(props);
this.state = {
data: [],
error: null,
};
}
// when component mounts run the function fetch
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = `http://www.ajaxlibrary.ca/?q=calendar-test`;
fetch(url)
.then((res) => res.json())
.then((res) => {
this.setState({
data: [...this.state.data, ...res.nodes],
error: res.error || null,
});
})
.catch(error => {
this.setState( error );
});
};
render() {
const { navigate } = this.props.navigation;
return (
<List containerStyle={{ marginTop: 0, borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
//squareAvatar
title={`${item.node.title}\n${item.node.Program_Location}`}
subtitle={item.node.Next_Session}
avatar={{ uri: item.node.Image }}
containerStyle={{ borderBottomWidth: 0 }}
// save params to pass to detailed events screen
onPress={() => navigate('Details', {title: `${item.node.title}`,
body: `${item.node.Body}`,
date: `${item.node.Date}`,
Next_Session: `${item.node.Next_Session}`,
Program_Location: `${item.node.Program_Location}`,
Nid: `${item.node.Nid}`,
Image: `${item.node.Image}`,
Run_Time: `${item.node.Run_Time}`})}
/>
)}
keyExtractor={item => item.node.Nid}
/>
</List>
);
}
}
class EventDetails extends Component {
static navigationOptions = {
title: 'EventDetails'
};
render() {
const { params } = this.props.navigation.state;
let pic = {
uri: `${params.Image}`
};
//const pic = { params.Image };
return (
<ScrollView>
<Card
title={params.title}
>
<Image
resizeMode="cover"
style={{ width: windowWidth * .85, height: windowHeight * 0.3}}
source={pic}
/>
<Button style={{marginTop: 10}}
icon={{name: 'date-range'}}
backgroundColor='#03A9F4'
fontFamily='Lato'
buttonStyle={{borderRadius: 0, marginLeft: 0, marginRight: 0, marginBottom: 0}}
title='Add to Calendar'
/>
<ListItem
title="Event Description"
subtitle={params.body}
hideChevron='true'
/>
<ListItem
title="Date"
subtitle={`${params.Next_Session}\n Run Time - ${params.Run_Time}`}
hideChevron='true'
/>
<ListItem
title="Location"
subtitle={params.Program_Location}
hideChevron='true'
/>
</Card>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
icon: {
width: 26,
height: 26,
},
});
const EventStack = StackNavigator({
EventList: {
screen: AplEvents,
navigationOptions: {
title: "APL Event Listing",
}
},
Details: {
screen: EventDetails,
},
TabsNav: { screen: MyHomeScreen}
});
const TabsNav = TabNavigator({
Home: {
screen: MyHomeScreen,
navigationOptions: {
tabBarLabel: 'Home',
tabBarIcon: ({ tintColor }) => (
<Image
source={{uri: 'https://upload.wikimedia.org/wikipedia/de/thumb/9/9f/Twitter_bird_logo_2012.svg/154px-Twitter_bird_logo_2012.svg.png'}}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
},
},
EventList: {
screen: EventStack,
navigationOptions: {
tabBarLabel: 'Events',
tabBarIcon: ({ tintColor }) => (
<Image
source={{uri: 'https://upload.wikimedia.org/wikipedia/de/thumb/9/9f/Twitter_bird_logo_2012.svg/154px-Twitter_bird_logo_2012.svg.png'}}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
},
},
}, {
tabBarOptions: {
activeTintColor: '#e91e63',
},
});
React Native Tab Navigation has an option prop as unmountOnBlur set it to true and it will reload the tab screens every time you click on tabs.
<Tab.Screen name={"Profile"} component={ProfileScreen} options={{unmountOnBlur: true}} />
Doc/Ref - RN Bottom tab Navigator docs
.
Update - There is a hook called as useIsFocused in '#react-navigation/native'.
Use this with useEffect to re-render the screen every time it is focused or used .
import { useIsFocused } from '#react-navigation/native';
const isFocused = useIsFocused();
useEffect(() => { yourApiCall(); }, [isFocused])
look at this link. My problem is solving thanks to this.
<Tabs.Navigator
initialRouteName="Home"
tabBar={(props) => (
<TabBar {...props} />
)}>
<Tabs.Screen
name="Home"
component={HomeView}
options={{ unmountOnBlur: true }}
listeners={({ navigation }) => ({
blur: () => navigation.setParams({ screen: undefined }),
})}
/>
</Tabs.Navigator>
https://github.com/react-navigation/react-navigation/issues/6915#issuecomment-692761324
There's many long discussion about this from react-native issue 314, 486, 1335, and finally we got a way to handle this, after Sep 27, 2017, react-navigation version v1.0.0-beta.13:
New Features
Accept a tabBarOnPress param (#1335) - #cooperka
So here we go,
Usage:
const MyTabs = TabNavigator({
...
}, {
tabBarComponent: TabBarBottom /* or TabBarTop */,
tabBarPosition: 'bottom' /* or 'top' */,
navigationOptions: ({ navigation }) => ({
tabBarOnPress: (scene, jumpToIndex) => {
console.log('onPress:', scene.route);
jumpToIndex(scene.index);
},
}),
});
I wasn't able to get this to work, and after checking the React Navigation documentation, found this, which seems to suggest that later versions (I'm using 1.0.0-beta.27) changed the method signature to a single object:
tabBarOnPress Callback to handle tap events; the argument is an object
containing:
the previousScene: { route, index } which is the scene we are leaving
the scene: { route, index } that was tapped
the jumpToIndex method
that can perform the navigation for you
https://reactnavigation.org/docs/en/tab-navigator.html#tabbaronpress
Given that, and the code from beausmith here, I put this together.
navigationOptions: ({ navigation }) => ({
tabBarOnPress: (args) => {
if (args.scene.focused) { // if tab currently focused tab
if (args.scene.route.index !== 0) { // if not on first screen of the StackNavigator in focused tab.
navigation.dispatch(NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: args.scene.route.routes[0].routeName }) // go to first screen of the StackNavigator
]
}))
}
} else {
args.jumpToIndex(args.scene.index) // go to another tab (the default behavior)
}
}
})
Note that you'll need to import NavigationActions from react-navigation for this to work.
Hope this helps somebody :)
I have two screen.
First screen is HomeScreen, second screen is ProfileScreen.
I used FlatList on HomeScreen and i wanna push to navigation to another screen. But when i used that codes, i saw that error message: "Can not read property 'navigate' of undefined"
Code like that
class ProfileScreen extends Component {
static navigationOptions = {
title: 'Profile',
};
render() {
const { navigate } = props.navigation;
return <Text>Hello, I am profile!</Text>;
}
}
class HomeScreen extends Component {
static navigationOptions = {
title: 'Home',
};
constructor(props) {
super(props);
this.state = {
data: [],
};
}
getScreen() {
this.props.navigation.navigate('Profile')
}
render() {
return (
<View>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<TouchableHighlight underlayColor= 'transparent' onPress= {this.getScreen}>
<View style= {{width: 300, height: 'auto'}} >
<Text> {item.title} </Text>
<View style= {{width: 300, height: 1, backgroundColor: 'red', marginBottom: 30, marginTop: 15}} />
</View>
</TouchableHighlight>
)}
/>
</View>
);
}
}
const AppNavigator = StackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen }
});
You're losing the context of this in your implementation. Fix it with function call:
renderItem={({ item }) => (
<TouchableHighlight underlayColor='transparent' onPress={() => this.getScreen()}>
...
</TouchableHighlight>
)}
In addition, you can use pattern:
const { navigate } = this.props.navigation;
navigate('Profile');
Inside the constructor bind this to your getScreen method.
Simply add following line inside constructor.
this.getScreen = this.getScreen.bind(this);