onPanResponderRelease not being triggered - react-native
I am trying to use the PanResponder on a View. The onStartShouldSetPanResponder and onMoveShouldSetPanResponder but onPanResponderMove, onPanResponderGrant and onPanResponderRelease does not get triggered at all. My react and react native versions are:
"react": "^15.2.1",
"react-native": "^0.30.0",
Below is the code
'use strict'
import React from 'react'
const Icon = require('react-native-vector-icons/Ionicons')
let THUMB_URLS = require('../Statics/ListingsData.js')
let SidePanelComponent = require('./common/SidePanel.js')
let RecentSearches = require('./Views/RecentSearches/RecentSearches.js')
let TimerMixin = require('react-timer-mixin')
const Loader = require('./common/LoadingState.js')
import { getImageURL, getUserImageURL } from './_helpers/images'
const config = require('../config')
import GoogleAnalytics from 'react-native-google-analytics-bridge'
GoogleAnalytics.setTrackerId(config.google_analytics_id)
const windowSize = require('Dimensions').get('window')
const deviceWidth = windowSize.width
const deviceHeight = windowSize.height
import {
Image,
Text,
View,
TouchableOpacity,
TouchableWithoutFeedback,
ScrollView,
StyleSheet,
Platform,
Animated,
PanResponder
} from 'react-native'
let LISTINGS = []
const ListingsViewComponent = React.createClass({
mixins: [TimerMixin],
getInitialState: function () {
return {
listings: [],
dataSource: [],
showSearchIcon: false,
showSidePanel: false,
photo: {},
componentloading: true,
showHeartIcon: [],
startX: 0,
startY: 0,
showWishlistMenu: false,
wishlistCurrentY: 0,
showNewWishlistTextInput: false,
currentRowdata: {},
wishlistOptions: [],
showrecentsearches: false,
isDataLoading: true,
scrolling: false,
_listViewDirtyPressEnabled: true,
scrollAnimationEnd: false,
scrollStates: [],
goingtonextview: false,
heroImageContainerHeight: deviceWidth,
searchbar: new Animated.ValueXY()
}
},
_panListingsResponder: {},
componentWillMount: function () {
this._panListingsResponder = PanResponder.create({
onStartShouldSetPanResponder: (e, g) => {
this.setState({
startX: e.nativeEvent.pageX,
startY: e.nativeEvent.pageY
})
},
onStartShouldSetPanResponderCapture: (e, g) => {
},
onMoveShouldSetPanResponder: (e, g) => {
this.setState({
heroImageContainerHeight: deviceWidth - (e.nativeEvent.pageY - this.state.startY)
})
},
onMoveShouldSetPanResponderCapture: (e, g) => {},
onPanResponderGrant: (e, g) => {},
onPanResponderMove: (e, g) => {
this.setState({
heroImageContainerHeight: deviceWidth - (e.nativeEvent.pageY - this.state.startY)
})
},
onPanResponderTerminationRequest: (e, g) => {
console.log('onPanResponderTerminationRequest', e.nativeEvent)
return false
},
onPanResponderRelease: (e, g) => {
console.log('_onResponderRelease', e.nativeEvent)
},
onPanResponderTerminate: (e, g) => {
console.log('onPanResponderTerminate', e.nativeEvent)
},
onShouldBlockNativeResponder: (e, g) => true
})
let listingsendpoint = 'http://faithstay-staging.herokuapp.com/api/listings'
this.setState({
isDataLoading: true
})
fetch(listingsendpoint)
.then((response) => response.json())
.then((listingsData) => {
const listings = listingsData
LISTINGS = []
LISTINGS.push(THUMB_URLS[0])
LISTINGS.push(THUMB_URLS[1])
LISTINGS.push(THUMB_URLS[2])
listings.map((listing) => {
LISTINGS.push(listing)
})
this.setState({
isDataLoading: false,
listings: LISTINGS
})
})
.catch((error) => {
console.warn(error)
})
},
componentDidMount: function () {
GoogleAnalytics.trackScreenView('Faithstay-Listings-Page')
},
_showSidePanel: function () {
this.setState({
showSidePanel: true
})
},
_closeSidePanel: function () {
this.setState({
showSidePanel: false
})
},
_showRecentSearches: function () {
this.setState({
showrecentsearches: true
})
},
_closeRecentSearches: function () {
this.setState({
showrecentsearches: false
})
},
componentWillReceiveProps: function () {
this.setState({
goingtonextview: false
})
},
getSearchBarStyle: function () {
return [
styles.searchbar, {
top: this.state.heroImageContainerHeight
}
]
},
render: function () {
let sidePanelViewContainer
if (this.state.showSidePanel) {
sidePanelViewContainer = (<SidePanelComponent {...this.props} imageuri={this.state.photo} onClose={this._closeSidePanel} />)
}
let searchIconContainer = <Animated.View style={this.getSearchBarStyle()}>
<TouchableOpacity style={styles.searchBarInner} onPress={this._showRecentSearches}>
<Text style={styles.searchtext}>
{'Where do you want to go?'}
</Text>
<Icon
name={'ios-search'}
size={30}
color={'#cfcfcf'}
style={styles.searchicon}
/>
</TouchableOpacity>
</Animated.View>
if (!this.state.showrecentsearches) {
if (this.state.isDataLoading) {
return (<Loader />)
} else {
return (
<View style={styles.container} {...this._panListingsResponder.panHandlers}>
<View style={[styles.heroImageContainer, { height: this.state.heroImageContainerHeight }]}>
<Image source={{uri: 'https://faithstay-statics.imgix.net/images/homepage_carousel_4.jpg'}} style={[styles.heroImage, { height: this.state.heroImageContainerHeight }]} />
<View style={[styles.scrimLayer, { height: this.state.heroImageContainerHeight }]} />
<View style={styles.logoContainer}>
<Image source={require('../Statics/images/anchor_3x.png')} style={styles.logoImage} />
<Text style={styles.logoText}>{'FaithStay'}</Text>
</View>
<View style={styles.horizontalDivider} />
<View style={styles.betaVersionContainer}>
<Text style={styles.betaVersionText}>{'Beta Version'}</Text>
</View>
<View style={[styles.pageTitleContainer, {top: this.state.heroImageContainerHeight - 85}]}>
<Text style={styles.pageTitle}>{'Home'}</Text>
</View>
<View style={[styles.movableScrim, {backgroundColor: `rgba(0, 0, 0, ${(deviceWidth - this.state.heroImageContainerHeight) / deviceWidth})`}]} />
</View>
{searchIconContainer}
<ScrollView style={styles.listView}>
{this.getListingsView()}
</ScrollView>
{sidePanelViewContainer}
</View>
)
}
}
return (<RecentSearches {...this.props} closeRecentSearches={this._closeRecentSearches} />)
},
_gotoUserProfilePage: function (user) {
this.props.navigator.push({
id: 15,
passProps: {
user
}
})
},
getListingsView: function () {
let listings = this.state.listings
const listingsArray = []
listings.map((listing, i) => {
let currentlisting = listing
let type = currentlisting.type
if (type !== 'NOT_A_LISTING') {
let imgSource = {
uri: getImageURL(currentlisting.images[0])
}
let profileimg = {
uri: getUserImageURL(currentlisting.host)
}
let title = currentlisting.title
let reviews = '18'
let address_values = currentlisting.google_place.formatted_address ? currentlisting.google_place.formatted_address.split(',') : []
let listing_address = {}
if (address_values.length > 0) {
listing_address = {
country: address_values[address_values.length - 1].trim(),
state: address_values[address_values.length - 2].trim(),
city: address_values[address_values.length - 3].trim()
}
}
let city = listing_address.city + ', ' + listing_address.state
let baseprice = currentlisting.base_price ? '$' + currentlisting.base_price : '0'
listingsArray.push(<View>
<TouchableWithoutFeedback onPress={() => this._pressRow(currentlisting)}>
<View>
<View style={styles.row}>
<Image style={styles.thumb} source={imgSource} >
<View style={styles.priceconatiner}>
<Text style={styles.pricetext}>{baseprice}</Text>
</View>
</Image>
</View>
<TouchableOpacity style={styles.profileImgContainer} onPress={() => this._gotoUserProfilePage(currentlisting.host)}>
<Image style={styles.profileimg} source={profileimg} />
</TouchableOpacity>
<View style={styles.listingtextcontainer}>
<Text style={styles.listingtexttitle}>{title}</Text>
<Text style={styles.listingtexttdescription}>{'Entire Home' + ' - ' + reviews + ' Reviews' + ' - ' + city}</Text>
</View>
</View>
</TouchableWithoutFeedback>
</View>)
} else {
let listing_title = listing.title
let listing_description = listing.description
let imageuri = listing.image;
listingsArray.push(<View><TouchableWithoutFeedback onPress={() => this._pressNonListingRow(currentlisting)}>
<View>
<View style={styles.rowNotListing}>
<Image style={styles.thumbNotListing} source={{uri: imageuri}}>
<View style={styles.thumbNotListing, {position: 'absolute', left:0, top: 0, right:0, bottom:0, backgroundColor: 'rgba(0,0,0,0.2)'}} >
</View>
<View style={styles.thumbNotListingSubContainer}>
<Text style={styles.listingtitle_notlisting}>{listing_title}</Text>
<Text style={styles.listingdescription_notlisting}>{listing_description}</Text>
</View>
</Image>
</View>
</View>
</TouchableWithoutFeedback>
</View>)
}
})
return listingsArray
},
_pressRow: function (listing) {
this.props.navigator.push({
id: 4,
passProps: {
listingdata: listing
}
})
},
_pressNonListingRow: function (listing) {
this.props.navigator.push({
id: 9,
passProps: {
filterData: listing
}
})
}
})
const paddingHorizontal = 15
const paddingVertical = 10
const distanceBetweenIcons = (deviceWidth - 115) / 3
const statusBarHeight = (Platform.OS === 'ios') ? 20 : 0
const isAndroid = Platform.OS === 'android'
const styles = StyleSheet.create({
listView: {
height: deviceHeight - 70,
top: (Platform.OS === 'ios') ? 40 : 0,
left: 0
},
scrimLayer: {
position: 'absolute',
top: 0,
left: 0,
width: deviceWidth,
height: deviceWidth,
backgroundColor: 'rgba(0, 0, 0, 0.2)'
},
movableScrim: {
position: 'absolute',
top: 0,
left: 0,
width: deviceWidth,
height: deviceWidth
},
container: {
flex: 1,
paddingTop: statusBarHeight,
width: deviceWidth,
height: deviceHeight
},
row: {
flexDirection: 'row',
justifyContent: 'center',
backgroundColor: '#f5f5f5',
width: deviceWidth,
height: deviceHeight / 2
},
separator: {
height: 1,
backgroundColor: '#CCCCCC'
},
thumb: {
width: deviceWidth,
height: deviceHeight / 2 - 80
},
thumbNotListing: {
width: deviceWidth,
height: deviceHeight / 2,
justifyContent: 'center'
},
thumbNotListingSubContainer: {
alignSelf: 'center',
justifyContent: 'center'
},
listingtitle_notlisting: {
textAlign: 'center',
alignSelf: 'center',
fontSize: 24,
fontWeight: 'bold',
color: '#ffffff'
},
listingdescription_notlisting: {
textAlign: 'center',
alignSelf: 'center',
fontSize: 16,
marginTop: 10,
color: '#ffffff'
},
text: {
flex: 1,
},
tabbar: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
width: deviceWidth,
height: 49,
backgroundColor: '#f5f5f5',
justifyContent: 'space-between',
borderTopWidth: 1,
borderTopColor: '#dce0e0'
},
searchbar: {
width: deviceWidth - 30,
height: 50,
left: 15,
top: deviceWidth - 5,
position: 'absolute',
justifyContent: 'center',
backgroundColor: '#f5f5f5',
shadowOpacity: 0.5
},
searchBarInner: {
width: deviceWidth - 30,
height: 50,
justifyContent: 'center',
backgroundColor: '#f5f5f5',
shadowOpacity: 0.5
},
searchonlyicon: {
width: 50,
height: 50,
borderRadius: 25,
left: 20,
top: 40,
position: 'absolute',
justifyContent: 'center',
backgroundColor: '#f5f5f5',
shadowOpacity: 0.5
},
searchtext: {
width: 160,
position: 'absolute',
fontSize: 15,
color: '#565a5c',
left: (deviceWidth - 30) / 2 - 80,
top: 15,
fontFamily: 'RobotoCondensed-Regular'
},
searchicon: {
width: 30,
height: 30,
position: 'absolute',
top: 8,
left: 12
},
homeicon: {
width: 30,
height: 30,
position: 'absolute',
top: paddingVertical - 2,
left: paddingHorizontal,
justifyContent: 'center',
},
hearticon: {
width: 40,
height: 30,
position: 'absolute',
top: paddingVertical,
justifyContent: 'center',
left: distanceBetweenIcons
},
emailicon: {
width: 45,
height: 30,
position: 'absolute',
top: paddingVertical,
justifyContent: 'center',
left: 2 * distanceBetweenIcons
},
bagicon: {
width: 35,
height: 20,
position: 'absolute',
top: paddingVertical + 6,
justifyContent: 'center',
left: 3 * distanceBetweenIcons
},
personicon: {
width: 30,
height: 30,
position: 'absolute',
top: paddingVertical,
justifyContent: 'center',
right: paddingHorizontal
},
priceconatiner: {
position: 'absolute',
top: deviceHeight / 2 - 150,
left: 0,
width: 60,
height: 40,
backgroundColor: 'rgba(60,63,64,0.9)',
justifyContent: 'center'
},
pricetext: {
fontSize: 20,
color: '#fff',
fontWeight: 'bold',
textAlign: 'center',
width: 60,
fontFamily: 'HelveticaNeue'
},
profileImgContainer: {
position: 'absolute',
top: deviceHeight / 2 - 108,
right: isAndroid ? 0 : 20, // NOTE: add to width, vs pushing it with position values
width: isAndroid ? 70 : 50, // NOTE: on android, the view must be as big as the image, otherwise the image will be cut off
height: 50,
paddingLeft: paddingHorizontal,
justifyContent: 'center'
},
profileimg: {
width: 50,
height: 50,
borderRadius: 25
},
listingtextcontainer: {
position: 'absolute',
top: deviceHeight / 2 - 70,
left: paddingHorizontal,
justifyContent: 'space-between',
height: 50
},
listingtexttitle: {
paddingTop: 5,
fontSize: 16,
fontFamily: 'HelveticaNeue',
color: '#565a5c',
fontWeight: 'bold'
},
listingtexttdescription: {
fontSize: 14,
fontFamily: 'HelveticaNeue',
color: '#82888a',
paddingBottom: 5
},
wishlistIcon: {
position: 'absolute',
right: 20,
top: 20
},
hearticonwishlist: {
width: 30,
height: 30
},
wishlistScrollView: {
position: 'absolute',
right: 20,
width: deviceWidth - 60,
height: 80,
backgroundColor: '#fff'
},
scrollRow: {
width: 180,
height: 40,
justifyContent: 'center',
padding: 5,
borderBottomWidth: 1,
borderBottomColor: '#f5f5f5'
},
wishlistScrollViewContainer: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
width: deviceWidth,
height: deviceHeight,
backgroundColor: 'rgba(255,255,255,0.1)'
},
touchableScrollViewContainer: {
width: deviceWidth,
height: deviceHeight,
position: 'absolute',
top: 0,
bottom: 0,
left: 0
},
fontWishlistScroller: {
color: '#565a5c',
fontSize: 14
},
rowNotListing: {
flexDirection: 'row',
justifyContent: 'center',
width: deviceWidth,
height: deviceHeight / 2
},
heroImageContainer: {
width: deviceWidth,
height: deviceWidth
},
heroImage: {
width: deviceWidth,
height: deviceWidth
},
logoContainer: {
width: 120,
position: 'absolute',
left: (deviceWidth / 2) - 60,
top: 19,
flexDirection: 'row',
justifyContent: 'center',
backgroundColor: 'transparent'
},
logoImage: {
width: 18,
height: 30,
top: 3
},
logoText: {
fontFamily: 'RobotoCondensed-Regular',
fontSize: 25,
fontWeight: '400',
textAlign: 'center',
color: '#fffff0',
marginLeft: 7.7
},
horizontalDivider: {
width: 32,
position: 'absolute',
left: deviceWidth / 2 - 16,
top: 59,
borderBottomWidth: 1,
borderColor: '#ffffff'
},
betaVersionContainer: {
width: 120,
position: 'absolute',
left: deviceWidth / 2 - 60,
top: 79,
justifyContent: 'center',
backgroundColor: 'transparent'
},
betaVersionText: {
fontFamily: 'RobotoCondensed-Regular',
fontSize: 14,
fontStyle: 'italic',
fontWeight: '300',
textAlign: 'center',
color: '#ffffff',
alignSelf: 'center'
},
pageTitleContainer: {
position: 'absolute',
top: deviceWidth - 85,
left: 20,
backgroundColor: 'transparent'
},
pageTitle: {
fontSize: 34,
fontFamily: 'RobotoCondensed-Bold',
color: '#ffffff'
}
})
module.exports = ListingsViewComponent
I got it working properly by using onPanResponderEnd instead of onPanResponderRelease.
Also if we still want to use onPanResponderRelease then we should allow termination request by:
onPanResponderTerminationRequest: () => true
You need to make sure the following handlers return true
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
The only different between onStart... and onMove... is that the PanResponder will be created when you start rendering the component for onStart..., it will be created (lazy) when user start tab or move for onMove.
On android side, you may still find that onPanResponderRelease will not be triggered, an issue reported here as well https://github.com/facebook/react-native/issues/9447
I ended up using onPanResponderTerminate to handle this case. Hopefully you can get more insights about it.
Related
how to use TapGestureHandler on Class Components
I'm trying to add a tap gesture to the render card of react-native-deck-swiper using the TapGestureHanlder from react-native-reanimated. Currently, it works when tapping the red zone outside the swiper, but I want to tap the picture and get the x position where I tapped. This is the code of the red zone, there I'm calling a class component in which I'm using the deck-swiper with some extra functions. const onSingleTapEvent = (event) => { if (event.nativeEvent.state === State.ACTIVE) { alert('Hey single tap!'); } }; return ( <GestureHandlerRootView style={{ zIndex: 10 }}> <TapGestureHandler onHandlerStateChange={onSingleTapEvent}> <AnimatedView style={styles.container}> <ImageSwiperDeck index={index} listOfAssetsWithinTheAlbum={listOfAssetsWithinTheAlbum} moveImageToTrashBin={moveImageToTrashBin} keepImageInAlbum={keepImageInAlbum} /> </AnimatedView> </TapGestureHandler> </GestureHandlerRootView> ); }; const styles = StyleSheet.create({ container: { height: 560, width: 500, zIndex: 10, backgroundColor: 'red', }, }); export default ImageSwiper; This is the deck-swiper code which works fine. return ( <Swiper ref={(swiper) => { this.swiper = swiper; }} cards={this.props.listOfAssetsWithinTheAlbum} cardIndex={this.props.index} renderCard={this.Card} backgroundColor={'transparent'} onSwipedLeft={this.deleteImage} onSwipedRight={this.keepImage} cardVerticalMargin={10} stackSize={5} stackScale={20} stackSeparation={5} animateOverlayLabelsOpacity animateCardOpacity disableTopSwipe disableBottomSwipe overlayLabels={{ left: { title: 'DELETE', style: { label: { backgroundColor: colors.red, borderColor: colors.red, color: colors.white, borderWidth: 1, fontSize: 24, }, wrapper: { flexDirection: 'column', alignItems: 'flex-end', justifyContent: 'flex-start', marginTop: 20, marginLeft: -20, }, }, }, right: { title: 'KEEP', style: { label: { backgroundColor: colors.blue, borderColor: colors.blue, color: colors.white, borderWidth: 1, fontSize: 24, }, wrapper: { flexDirection: 'column', alignItems: 'flex-start', justifyContent: 'flex-start', marginTop: 20, marginLeft: 20, }, }, }, }} /> );
react native why my absolute view is not showing all only the half?
why my purple absolute view is not showing all, only the half ? look at the image Code: CardView: const { width } = Dimensions.get('window'); const MAX_NAME_WIDTH = width / 2 - 48; const CARD_WIDTH = width * 0.45; const UserCardAdvanced = ({ user_id, username, profile_image, followingShops, is_verification, isD, stars, onPress, onPressReviews, onPressFollow }: IUserCardAdvanced) => { return ( <Pressable onPress={onPress} style={s.card}> { isD && <PurpleNotice /> } <Image source={{uri: profile_image}} resizeMode='contain' style={s.profile_image} /> <View style={s.starContainer}> <Stars star_filled={stars?.fill} disabled is_all size={15} onPressAll={onPressReviews} /> <Text style={s.reviewTotal}>{stars?.total_reviews}</Text> </View> <View style={s.nameContainer}> <NameWithVerification name={username} is_verification={is_verification} nameStyle={s.nameStyle} /> </View> <FollowBtn onPress={onPressFollow} is_followed={false} size={24} /> </Pressable> ) } const s = StyleSheet.create({ card: { padding: 12, backgroundColor: '#fff', borderRadius: 8, elevation: 3, margin: 6, justifyContent: 'center', alignItems: 'center', marginBottom: 20, zIndex: 99921, width: CARD_WIDTH }, starContainer: { flexDirection: 'row', alignItems: 'center', marginTop: 8 }, reviewTotal: { fontFamily: globalStyles.font_regular, color: '#333', marginLeft: 4 }, nameContainer: { marginVertical: 8 }, nameStyle: { color: '#333', fontSize: 13, maxWidth: MAX_NAME_WIDTH }, profile_image: { height: 84, width: 84, borderRadius: 50, }, username: { fontFamily: globalStyles.font_regular, color: '#555', marginTop: 12, textAlign: 'center' } }) Purple Notice const PurpleNotice = (isD: isD) => { return ( <View style={s.view}> <Text style={s.text}>TESTTESTETS</Text> </View> ) } const s = StyleSheet.create({ view: { padding: 2, paddingHorizontal: 10, backgroundColor: globalStyles.globalColor, borderRadius: 6, position: 'absolute', top: -10, left: 0, zIndex: 99999 }, text: { fontFamily: globalStyles.font_light, color: '#fff', fontSize: 11, textTransform: 'uppercase', textShadowColor: '#fff', textShadowOffset: { height: 2, width: 2 } } }); I dont know what I am doing wrong i am very thankful for your help!! ......................................... ......................................... ......................................... ......................................... .........................................
You can add zIndex={1} to that purple view.
use right:0 in style const s = StyleSheet.create({ view: { padding: 2, paddingHorizontal: 10, backgroundColor: globalStyles.globalColor, borderRadius: 6, position: 'absolute', top: -10, left: 0, right:0, //use this also zIndex: 99999 }, text: { fontFamily: globalStyles.font_light, color: '#fff', fontSize: 11, textTransform: 'uppercase', textShadowColor: '#fff', textShadowOffset: { height: 2, width: 2 } } });
Android scrollview doesnt scroll and cuts off text
I have a scrollview and I want it to be able to scroll text in a certain part of the screen (the rest of the screen should not be able to scroll) However, the scrollview doesn't scroll and the text inside of it gets cut off, and most of it doesn't show. When I remove the scrollview, the full-text shows. Here is a link to how the screen looks like https://imgur.com/a/CVnLRyi Fyi, the points variable contains a huge chunk of text Below is my code: import { StyleSheet, View, Text,FlatList, TouchableOpacity, ScrollView } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { Lesson } from "../../App"; const data = require('../Data/MainData.json').mainData function Flashcards({route, navigation}) { let index = [] let counter = 0 let points = "" const { PrimaryType } = route.params.params; const {TopicName} = route.params.params; const {Elements} = route.params.params; const {Chapter} = route.params.params; // setting up variables... for (var i = 0; i <= Elements.length-1; i++){ if (data[Elements[i]].topic.toLowerCase() == Chapter.toLowerCase()){ index.push(Elements[i]) } } for (var i = 1; i <= 20; i++){ if (data[index[counter]][`point${i}`] != "Empty Cell"){ points += `${data[index[counter]][`point${i}`]}\n\n` } } return ( // un-scrollable section <View style = {styles.container}> // UI starts here <View style={styles.rect1Stack}> <View style={styles.rect1}> <Text style={styles.lowerPrimary}>{PrimaryType}</Text> <Text style={styles.syllabus}>Primary School {TopicName}</Text> </View> <View style={styles.rect2}></View> </View> // scrollable text section <ScrollView style = {styles.ScrollView}> <Text style={styles.conceptName}>{data[index[counter]].concepts}</Text> <Text style={styles.info}>{points}</Text> </ScrollView> </View> ); } const styles = StyleSheet.create({ container: { flex: 1 }, rect1: { top: 0, height: 207, position: "absolute", backgroundColor: "rgba(247,142,105,1)", left: 3, right: 4 }, lowerPrimary: { fontFamily: "roboto-700", color: "rgba(255,255,255,1)", fontSize: 26, height: 88, width: 286, marginTop: 30, marginLeft: 20 }, rect2: { top: 148, left: 0, height: 83, position: "absolute", backgroundColor: "rgba(255,255,255,1)", borderRadius: 43, right: 0 }, rect1Stack: { height: 231, marginLeft: -3, marginRight: -4 }, syllabus: { fontFamily: "roboto-700", color: "rgba(255,255,255,1)", fontSize: 20, height: 35, width: 286, marginTop: -30, marginLeft: 20 }, yellowrec: { //unused property height: 400, backgroundColor: "rgba(255,231,185,1)", marginTop: 0, marginLeft: 20, marginRight: 10 }, conceptName: { fontFamily: "roboto-regular", color: "#121212", fontSize: 18, height: 80, marginTop: 10, textAlign: "center", marginLeft: 0, marginRight: 0, alignSelf: "center" }, info: { position: "absolute", fontFamily: "roboto-regular", color: "#121212", top: 50, left: 5, width: 360, fontSize: 18, marginLeft: 10, }, ScrollView: { top: 0, left: 0, marginRight: 10, height: 7000, marginLeft: 10, backgroundColor: "rgba(255,231,185,1)" } }); export default Flashcards;```
You can Just the split screen into two by using the flex eg: container: { flex: 1, }, rect1Stack:{ flex:.4, backgroundColor:'green', }, scrollView:{ flex:.6, backgroundColor:'blue' },
Show a custom pop-up alert overlay on-top of header - React Navigation
I'm trying to show this custom modal pop-up over the top of the Header component in React Navigation. See the pic below: I've tried setting zIndexes on the overlay and parent view containers, but the header is seperate in the component stack. Anyone know how to solve this? I've tried putting a BlurView over the header component. import React, { Component } from "react"; import PropTypes from "prop-types"; import { StyleSheet, View, Animated, Text, TextInput } from "react-native"; import BlockButton from "../common/BlockButton"; import { BlurView } from "react-native-blur"; export default class BlurAlert extends Component { static navigationOptions = ({ navigation }) => { return { headerStyle: {} }; }; constructor(props) { super(props); this.state = { visible: this.props.visible, title: null, body: null }; } render() { if (this.props.visible) { return ( <View style={styles.wrapper}> <BlurView style={styles.blurView} blurType="dark" blurAmount={5} /> <View style={styles.box}> <Text style={styles.titleText}>{this.props.title}</Text> <Text style={styles.bodyText}>{this.props.body}</Text> {this.props.children} <BlockButton backgroundColor={"#F0AC8F"} borderColor={"#F0AC8F"} fontColor={"white"} title={this.props.buttonText} padding={23} marginTop={25} disabled={false} onPress={this.props.onClosed} /> </View> </View> ); } else { return <View />; } } } BlurAlert.propTypes = { title: String, body: String, buttonText: String, visible: Boolean }; BlurAlert.defaultProps = { visible: false, buttonText: "OK" }; const styles = StyleSheet.create({ wrapper: { position: "absolute", top: 0, left: 0, bottom: 0, right: 0, height: "100%", width: "100%", zIndex: 9999 }, blurView: { flex: 1, zIndex: 99999 }, container: { margin: 25, zIndex: 99999, position: "relative", top: 0, left: 0, bottom: 0, right: 0 }, box: { backgroundColor: "white", padding: 20, position: "absolute", top: "15%", borderColor: "black", borderWidth: StyleSheet.hairlineWidth, margin: 25, alignItems: "center", shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.1, shadowRadius: 3, zIndex: 99999, width: "85%" }, titleText: { fontFamily: "Lora", fontSize: 20 }, bodyText: { marginTop: 15, textAlign: "center", fontFamily: "Lora", fontSize: 16, lineHeight: 20 } });
You can try using "Modal": import React, { Component } from "react"; import PropTypes from "prop-types"; import { StyleSheet, View, Animated, Text, TextInput, Modal } from "react-native"; import BlockButton from "../common/BlockButton"; import { BlurView } from "react-native-blur"; export default class BlurAlert extends Component { static navigationOptions = ({ navigation }) => { return { headerStyle: {} }; }; constructor(props) { super(props); this.state = { visible: this.props.visible, title: null, body: null }; } render() { if (this.props.visible) { return ( <Modal transparent animationType="fade" visible={this.state.visible} onRequestClose={() => { console.log('Modal has been closed.'); }}> > <View style={styles.wrapper}> <BlurView style={styles.blurView} blurType="dark" blurAmount={5} /> <View style={styles.box}> <Text style={styles.titleText}>{this.props.title}</Text> <Text style={styles.bodyText}>{this.props.body}</Text> {this.props.children} <BlockButton backgroundColor={"#F0AC8F"} borderColor={"#F0AC8F"} fontColor={"white"} title={this.props.buttonText} padding={23} marginTop={25} disabled={false} onPress={this.props.onClosed} /> </View> </View> </Modal> ); } else { return <View />; } } } BlurAlert.propTypes = { title: String, body: String, buttonText: String, visible: Boolean }; BlurAlert.defaultProps = { visible: false, buttonText: "OK" }; const styles = StyleSheet.create({ wrapper: { position: "absolute", top: 0, left: 0, bottom: 0, right: 0, height: "100%", width: "100%", zIndex: 9999 }, blurView: { flex: 1, zIndex: 99999 }, container: { margin: 25, zIndex: 99999, position: "relative", top: 0, left: 0, bottom: 0, right: 0 }, box: { backgroundColor: "white", padding: 20, position: "absolute", top: "15%", borderColor: "black", borderWidth: StyleSheet.hairlineWidth, margin: 25, alignItems: "center", shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.1, shadowRadius: 3, zIndex: 99999, width: "85%" }, titleText: { fontFamily: "Lora", fontSize: 20 }, bodyText: { marginTop: 15, textAlign: "center", fontFamily: "Lora", fontSize: 16, lineHeight: 20 } });
React Native Button in ScrollView positioning
I have another problem with a button. I have to position it inside a ListView under the last item. Here are the classes I used: Notes.js: import React, { Component } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, } from 'react-native'; export default class Note extends Component { render() { return ( <View key={this.props.keyval} style={styles.note}> <Text style={styles.noteText}>{this.props.val.date}</Text> <Text style={styles.noteText}>{this.props.val.note}</Text> <TouchableOpacity onPress={this.props.deleteMethod} style={styles.noteDelete}> <Text style={styles.noteDeleteText}>Del</Text> </TouchableOpacity> </View> ); } } const styles = StyleSheet.create({ note: { position: 'relative', padding: 20, paddingRight: 100, borderBottomWidth:2, borderBottomColor: '#ededed' }, noteText: { paddingLeft: 20, borderLeftWidth: 10, borderLeftColor: '#0000FF' }, noteDelete: { position: 'absolute', justifyContent: 'center', alignItems: 'center', backgroundColor: '#2980b9', padding: 10, top: 10, bottom: 10, right: 10 }, noteDeleteText: { color: 'white' } }); This is the component that I use every time when I want to create a note. Main.js: import React, { Component } from 'react'; import { View, Text, StyleSheet, TextInput, ScrollView, TouchableOpacity, AsyncStorage, } from 'react-native'; import Note from './Note'; export default class Main extends Component { constructor(props){ super(props); this.state = { noteArray: [], noteText: '', }; this.getSavedNotes(this.state.noteArray); } render() { let notes = this.state.noteArray.map((val, key)=>{ return <Note key={key} keyval={key} val={val} deleteMethod={()=>this.deleteNote(key)}/> }); return ( <View style={styles.container}> <View style={styles.header}> <Text style={styles.headerText}>- NOTER -</Text> </View> <ScrollView style={styles.scrollContainer}> {notes} <TouchableOpacity onPress={ this.addNote.bind(this) } style={styles.addButton}> <Text style={styles.addButtonText}>+</Text> </TouchableOpacity> </ScrollView> <View style={styles.footer}> <TextInput style={styles.textInput} placeholder='Write your note here' onChangeText={(noteText)=> this.setState({noteText})} value={this.state.noteText} placeholderTextColor='white' underlineColorAndroid='transparent'> </TextInput> </View> </View> ); } addNote(){ if(this.state.noteText){ var d = new Date(); this.state.noteArray.push({ 'date':d.getFullYear()+ "/"+(d.getMonth()+1) + "/"+ d.getDate(), 'note': this.state.noteText }); this.setState({ noteArray: this.state.noteArray }); this.setState({noteText:''}); AsyncStorage.setItem('arr', JSON.stringify(this.state.noteArray)); alert(this.state.noteArray); } } deleteNote(key){ this.state.noteArray.splice(key, 1); this.setState({noteArray: this.state.noteArray}); } getSavedNotes = async (noteArray) =>{ try{ let data = await AsyncStorage.getItem('arr'); if(JSON.parse(data)) { this.state.noteArray = JSON.parse(data);v } }catch(error){ alert(error); } } } const styles = StyleSheet.create({ container: { flex: 1, }, header: { backgroundColor: '#1A237E', alignItems: 'center', justifyContent:'center', borderBottomWidth: 10, borderBottomColor: '#ddd' }, headerText: { color: 'white', fontSize: 18, padding: 26 }, scrollContainer: { flex: 1, marginBottom: 100 }, footer: { position: 'absolute', bottom: 0, backgroundColor: '#000000', left: 0, right: 70, zIndex: 10 }, textInput: { alignSelf: 'stretch', color: '#fff', padding: 20, backgroundColor: '#252525', borderTopWidth:2, borderTopColor: '#ededed' }, addButton: { position: 'absolute', zIndex: 11, right: 0, bottom: 0, backgroundColor: '#1A237E', width: 70, height: 68, // borderRadius: 35, alignItems: 'center', justifyContent: 'center', elevation: 8 }, addButtonText: { color: '#fff', fontSize: 24 } }); Here is where I save the notes and display them inside the ListView. After the insertion, the button should appear under the new added note. Finally the App.js: import React, { Component } from 'react'; import Main from './app/components/Main.js'; export default class App extends Component { render() { return ( <Main/> ); } } Here I just display the Main.js component.
I made it! Here are the changes I made in Main.js file: <ScrollView style={styles.scrollViewContainer}> <ScrollView style={styles.scrollContainer}> {notes} </ScrollView> <TouchableOpacity onPress={ this.addNote.bind(this) } style={styles.addButton}> <Text style={styles.addButtonText}>+</Text> </TouchableOpacity> </ScrollView> And here is the designSheet: const styles = StyleSheet.create({ container: { flex: 1, }, header: { backgroundColor: '#1A237E', alignItems: 'center', justifyContent:'center', borderBottomWidth: 10, borderBottomColor: '#ddd', }, headerText: { color: 'white', fontSize: 18, padding: 26 }, scrollContainer: { flex: 1, }, footer: { position: 'absolute', bottom: 0, height: 70, backgroundColor: '#000000', left: 0, right:0, zIndex: 10, }, textInput: { alignSelf: 'stretch', color: '#fff', padding: 20, backgroundColor: '#252525', borderTopWidth:2, borderTopColor: '#ededed', marginRight: 70, }, addButton: { position: 'relative', zIndex: 11, left: 0, top: 0, backgroundColor: '#1A237E', width: 70, height: 70, alignItems: 'center', justifyContent: 'center', elevation: 8 }, addButtonText: { color: '#fff', fontSize: 60 }, scrollViewContainer: { flex: 1, marginBottom: 70, } }); I hope this help others aswell