React native drawer not opening on swipe over map - react-native

I am using react-native-navigation to start a single screen app that contains a Google map using - react-native-maps and a left drawer:
Navigation:
Navigation.startSingleScreenApp({
screen: {
screen : 'map.MapScreen',
title : 'Map',
navigatorStyle: {
navBarHidden: true
}
},
drawer : {
left : {
screen : 'drawer.DrawerScreen',
passProps: {}
},
disableOpenGesture: true
},
animationType: 'slide-down',
passProps: {}
})
MapScreen:
export default class MapScreen extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={styles.container}>
<MapView
style={styles.map}
provider={PROVIDER_GOOGLE}>
</MapView>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
zIndex: -1
},
map: {
...StyleSheet.absoluteFillObject,
},
});
Unfortunately if I try to open the drawer, by swiping to the right, over the map, it won't open.
Does anyone have any idea on how to fix this?

You can use an overlay to add some invisible edge on the side of the map that does capture the gesture for opening the drawer.
looks something like this:
const Screen = {
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
};
class Foo extends React.Component<Props, object> {
render() {
return (
<View style={styles.container}>
<MapView
style={styles.mapContainer}
/>
<View style={styles.mapDrawerOverlay} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
mapContainer: {
width: Screen.width,
height: Dimensions.get('window').height,
},
mapDrawerOverlay: {
position: 'absolute',
left: 0,
top: 0,
opacity: 0.0,
height: Dimensions.get('window').height,
width: 10,
},
});
This will use an overlay that is transparent and covers a small fraction of the map view. Beginning a drag-gesture in this area now can trigger the drawer.

Do not use ...StyleSheet.absoluteFillObject on your map styles.
Doing this will resolve your issue
const React = require("react-native");
const { StyleSheet, Dimensions } = React;
const { width, height } = Dimensions.get("window");
export default {
map: {
width: width,
height: height
}
};

You need to activate ToggleDrawer() when you hit the side of the map which is covered by a thin TouchableOpacity window. here is the example code in homeview. make sure to bring in props as a variable to your function.
import React from 'react';
import {View, Text, SafeAreaView, StyleSheet, Dimensions, TouchableOpacity} from 'react-native';
import MapView, {Marker} from 'react-native-maps';
const HomeScreen = (props) => {
return(
<SafeAreaView style = {{flex: 1}}>
<View style = {styles.container}>
<MapView
style={styles.mapStyle}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
}
/>
</MapView>
<TouchableOpacity style = {styles.mapDrawerOverlay} onPress = {() => {
props.navigationProps.toggleDrawer();}
} />
</View>
</SafeAreaView>
);
};
export default HomeScreen;
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'flex-end',
},
mapDrawerOverlay:{
position: 'absolute',
left: 0,
top: 0,
opacity: 0.0,
height: Dimensions.get('window').height,
width:10,
},
mapStyle: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
});

Related

react-native-maps MapView rotation

I want to rotate the MapView as per the phone rotation. I am not getting a solution in official documentation, so I did a trick to do it by rotating the layout by setting the transform style but I can understand it is not the perfect solution because the labels over the screen also rotating with the layout.
If it is possible by animateCamera, Please suggest me how can I update the code.
I have attached a screenshot of the issue and relevant code block.
import MapView from 'react-native-maps';
import { Bubbles, DoubleBounce } from 'react-native-loader';
class TestPropUpdate extends Component {
constructor(props) {
super(props);
this.state = {
origin: null,
latlong: null,
initialRegion: null,
compasAngle: 0
};
}
componentDidUpdate() {
CompassHeading.start(3, ({ heading, accuracy }) => {
if (this.state.compasAngle !== heading) {
this.setState({ compasAngle: heading })
console.log(heading)
}
CompassHeading.stop();
})
}
getCurrentLocation = () => ({
latitude: this.state.latlong.currentLatitude,
longitude: this.state.latlong.currentLongitude,
latitudeDelta: 0.01,//0.01155,
longitudeDelta: 0.01//0.01155
});
setCurrentLocation = (locationChangedResult) => {
this.setState({
latlong: {
"currentLatitude": locationChangedResult.nativeEvent.coordinate.latitude,
"currentLongitude": locationChangedResult.nativeEvent.coordinate.longitude
}
})
}
render() {
return (
<SafeAreaView style={{ flex: 1, position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center' }}>
{this.state.latlong ?
<MapView
onUserLocationChange={locationChangedResult => this.setCurrentLocation(locationChangedResult)}
region={this.getCurrentLocation()}
style={[
styles.mapStyle,
{
transform: [{ rotate: `${360 - this.state.compasAngle}deg` }],
}
]}
>
</MapView>
: <DoubleBounce />}
</SafeAreaView>
);
}
}
export default TestPropUpdate;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
padding: 10,
alignItems: 'center',
justifyContent: 'center',
},
boldText: {
fontSize: 25,
color: 'red',
marginVertical: 16,
textAlign: 'center'
},
mapStyle: {
position: 'absolute',
width: "200%",
height: "100%",
}
});

Playing of a video blo

The next program in React-Native allows a video to play but blocks the clicking on the button. I believe this is because of the position:absolute value but is there a way to avoid this problem?
import React, { Component } from 'react';
import Video from 'react-native-video';
import {
StyleSheet,
TouchableOpacity,
Text,
View,
Button,
} from 'react-native'
class App extends Component {
state = {
count: 0,
message: 'Hello Everybody'
}
onPress = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
<View style={styles.container}>
<Button title={'Press me'}
onPress={() => {
console.log('Boton Presionado')
this.setState({message: 'Presiono'})
}}
/>
<Text>{this.state.message}</Text>
<Video source={{uri: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'}}
ref={(ref) => {
this.player = ref
}} // Store reference
onBuffer={this.onBuffer} // Callback when remote video is buffering
onError={this.videoError} // Callback when video cannot be loaded
style={styles.backgroundVideo}
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
button: {
alignItems: 'center',
backgroundColor: '#DDDDDD',
padding: 10,
marginBottom: '10'
},
backgroundVideo: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
}
})
export default App;
I changed position: 'absolute' to position: 'relative' and added values for height and width.

How to slide down a View in react native

Hi I am new to react native
I have mapview and FlatList in my screen like shown in the below image.
Now I want Mapview to be expanded when i click on it , like the below image
Here is my code
import React, { Component } from 'react';
import MapView, { PROVIDER_GOOGLE, Marker } from 'react-native-maps';
import { View, StyleSheet, Text, TouchableOpacity, Animated, Image } from 'react-native';
import { Actions } from 'react-native-router-flux';
import DoctorsList from './DoctorsList';
import colors from '../styles/colors';
var isHidden = true;
export default class FindDoctorMaps extends Component {
constructor(props) {
super(props);
this.state = {
bounceValue: new Animated.Value(500), //This is the initial position of the subview
buttonText: 'LIST VIEW',
printText: false,
markers: [{
title: 'hello',
coordinates: {
latitude: 17.452,
longitude: 78.3721
},
},
{
title: 'hi',
coordinates: {
latitude: 17.458,
longitude: 78.3731
},
},
{
title: 'gyp',
coordinates: {
latitude: 17.468,
longitude: 78.3631
},
},
{
title: 'nig',
coordinates: {
latitude: 17.568,
longitude: 78.3831
},
},
{
title: 'Yoy',
coordinates: {
latitude: 17.588,
longitude: 78.4831
},
}]
};
}
render() {
return (
<View style={{ flex: 1, backgroundColor: 'white' }} >
<MapView
provider={PROVIDER_GOOGLE}
region={{
latitude: 17.452,
longitude: 78.3721,
latitudeDelta: 0.015,
longitudeDelta: 0.0121,
}}
style={styles.map}
showsUserLocation
followUserLocation
showsMyLocationButton
showsCompass
zoomEnabled
>
{this.state.markers.map(marker => (
<MapView.Marker
coordinate={marker.coordinates}
title={marker.title}
onPress={this.onMarkerPress}
/>
))}
</MapView>
<TouchableOpacity style={styles.filterContainer} onPress={Actions.morefilters}>
<View style={styles.filterStyle}>
<Image style={{ margin: 5 }} source={require('../assets/filters_icon.png')} />
<Text style={{ fontSize: 14, color: colors.darkBlue, fontWeight: 'bold', }}> Filters</Text>
</View>
</TouchableOpacity>
<View>
<TouchableOpacity >
<DoctorsList />
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
map: {
height: 200,
},
textViewStyle: {
fontSize: 14,
color: '#00539d',
fontWeight: 'bold',
margin: 20
},
subView: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
backgroundColor: '#FFFFFF',
height: 500,
},
filterContainer: {
top: '20%',
right: 0,
alignSelf: 'flex-end',
position: 'absolute'
},
filterStyle: {
flexDirection: 'row',
backgroundColor: colors.white,
borderRadius: 8,
height: 30,
width: '60%',
alignItems: 'center',
justifyContent: 'center'
}
});
Can any one please help me how to expand Mapview by clicking on it.
or else please share some useful links so that i would be helpful for me
this.state = {
....
showMapFullScreen: false
....
}
render() {
....
<MapView
....
style={height: showMapFullScreen ? "100%" : 200 }
onPress={() => this.setState({ showMapFullScreen : true }) }
....
>
....
<TouchableOpacity
onPress={() => this.setState({ showMapFullScreen : false }) }>
<DoctorsList />
</TouchableOpacity>
.......
}
You can toggle the map height with a flag like shown above. When the user clicks on the map the height becomes full screen when he/she clicks on the list the map's height is reduced and gives space to the list to render.

Saving Data And Sending To Another Class In React Native

I have a little problem with an app that I am working on. I have to make something like a note app. There for I have a button that navigates me to another screen where I can write the note and I have a save button that should send me back to the previews screen and the scrollview shold be updated
Here is what I've tried so far:
App.js:
import React, { Component } from 'react';
import Main from './app/components/Main.js';
import NoteBody from './app/components/NoteBody.js';
import {StackNavigator} from 'react-navigation'
const App = StackNavigator({Home: {screen: Main}, WriteNote: {screen: NoteBody}});
export default App;
Main.js:
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
ScrollView,
TouchableOpacity,
AsyncStorage,
} from 'react-native';
import Note from './Note';
import NoteBody from './NoteBody.js';
export default class Main extends Component {
static navigationOptions = {
title: 'Notes',
};
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: '',
};
}
componentDidMount(){
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)}/>
});
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<ScrollView style={styles.scrollViewContainer}>
<ScrollView style={styles.scrollContainer}>
{notes}
</ScrollView>
<TouchableOpacity onPress={() =>
navigate('WriteNote' ,{onNavigateBack:
this.handleOnNavigateBack.bind(this)})}
style={styles.addButton}>
<Text style={styles.addButtonText}>+</Text>
<Text style={styles.addButtonAditionalText}>Add note</Text>
</TouchableOpacity>
</ScrollView>
</View>
);
}
deleteNote(key){
this.state.noteArray.splice(key, 1);
this.setState({noteArray: this.state.noteArray});
AsyncStorage.setItem('arr', JSON.stringify(this.state.noteArray));
// alert(this.state.noteArray);
}
getSavedNotes = async (noteArray) =>{
try{
let data = await AsyncStorage.getItem('arr');
if(JSON.parse(data))
{
this.setState({noteArray: JSON.parse(data)});
}
}catch(error){
alert(error);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
scrollContainer: {
flex: 1,
},
addButton: {
position: 'relative',
zIndex: 11,
left: 0,
top: 0,
alignItems: 'flex-start',
justifyContent: 'flex-end',
width: 100,
height: 60,
elevation: 8
},
addButtonText: {
color: '#000',
fontSize: 60,
},
addButtonAditionalText: {
color: '#000',
fontSize: 12,
marginLeft: 40,
position: 'absolute',
bottom: 20,
},
scrollViewContainer: {
flex: 1,
marginBottom: 70,
}
});
Note.js: Here we have the scrollview and the button that navigates you to the NoteBody screen.
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'
}
});
and finally NoteBody: Here is where you can write the body of the note and you have that save button that should also save the data in an AsyncStorage so I can display it even after the app is closed.
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity,
AsyncStorage,
} from 'react-native';
import Note from './Note.js';
export default class NoteBody extends Component {
static navigationOptions = {
title: 'Note',
};
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: '',
};
}
componentDidMount(){
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.noteBody}>
<TextInput
multiline = {true}
numberOfLines = {1000000}
style={styles.textInput}
placeholder='Write your note here'
onChangeText={(noteText)=> this.setState({noteText})}
value={this.state.noteText}
placeholderTextColor='grey'
underlineColorAndroid='transparent'>
</TextInput>
</View>
<TouchableOpacity onPress={ this.addNote.bind(this) } style={styles.addButton}>
<Text style={styles.addButtonText}>SAVE</Text>
</TouchableOpacity>
</View>
);
}
addNote(){
const { navigate } = this.props.navigation;
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));
this.props.navigation.state.params.onNavigateBack();
navigate('Home');
// alert(this.state.noteArray);
}
}
getSavedNotes = async (noteArray) =>{
try{
let data = await AsyncStorage.getItem('arr');
if(JSON.parse(data))
{
this.setState({noteArray: JSON.parse(data)});
}
}catch(error){
alert(error);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
noteBody:{
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
zIndex: 10,
alignItems: 'center',
borderBottomWidth:1,
borderTopColor: '#000',
marginBottom: 100,
},
textInput: {
alignSelf: 'stretch',
textAlignVertical: 'top',
backgroundColor: '#fff',
color: '#000',
padding: 20,
borderTopWidth:2,
borderTopColor: '#ededed',
},
addButton: {
position: 'absolute',
zIndex: 11,
left: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center',
width: 300,
backgroundColor: '#00FF00',
height: 60,
elevation: 8
},
addButtonText: {
color: '#fff',
fontSize: 24,
},
});
The save button only saves the last note I wrote and it doesn't even show it on the scrollview immediately. I have to reopen the app to display it.
There are a couple of things which are wrong here:
First you only fetch the saved notes in the constructor of your Main component, which only gets called on instantiation. The recommended place to put this code is in componentDidMount(). Also I don't understand why you are are passing the noteArray state to your getSavedNotes() method.
Next, and this is an important one: your methods to add and delete notes from the array are mutating (splice and push). Since React uses shallow comparison to determine when to re-render, you need to create a new object when using setState(). So for additions you could use concat() and for deletions the omit() function of Lodash is very useful. Alternatively you could use the spread operator.
Disclaimer: I haven't yet read all of your code in detail, so there might still be some additional problems.
Edit: with FlatList
// render() method of Main.js
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<ScrollView style={styles.scrollViewContainer}>
<ScrollView style={styles.scrollContainer}>
<FlatList
data={this.state.noteArray}
renderItem={({item, index}) => this.renderItem(item, index)}
keyExtractor={(item, index) => `${index}`}
/>
</ScrollView>
<TouchableOpacity onPress={() =>
navigate('WriteNote')}
style={styles.addButton}>
<Text style={styles.addButtonText}>+</Text>
<Text style={styles.addButtonAditionalText}>Add note</Text>
</TouchableOpacity>
</ScrollView>
</View>
);
}
renderItem = (item, index) => {
return (
<Note
keyval={index}
val={item}
deleteMethod={()=>this.deleteNote(index)}
/>
);
}
It looks like you could add a listener in Main.js for 'onFocus' events. I would use the callback to query AsyncStorage and set state with the result.
Screen A
Method to do the thing in Screen A that you want to happen when navigating back from Screen B:
handleOnNavigateBack = (foo) => {
this.setState({
foo
})
}
This is the React Navigation method in Screen A to navigate to Screen B:
this.props.navigation.navigate('ScreenB', {
onNavigateBack: this.handleOnNavigateBack})
…or if your handleOnNavigateBack function is not a arrow function (which binds this) you'll need to bind this: (ht #stanica)
this.props.navigation.navigate('ScreenB', {
onNavigateBack: this.handleOnNavigateBack.bind(this)
})
Screen B
After navigating to Screen B and ready to Navigate back to Screen A…
Call the function (passed to Screen B from Screen A) to do the thing in Screen A:
this.props.navigation.state.params.onNavigateBack(this.state.foo)
Then call the React Navigation method to go Back (to Screen A):
this.props.navigation.goBack()
I also updated my question's code so it is the version that dose what is suppose to do.

Hide TabNavigators and Header on Scroll

I want to hide the Header and the TabNavigator tabs onScroll. How do I do that? I want to hide them onScroll and show them on ScrollUp. My code:
import React, { Component } from 'react';
import { View, Text, ScrollView, StyleSheet, TouchableOpacity} from 'react-native';
class ScrollTest extends Component {
render(){
const { params } = this.props.navigation.state;
return(
<View style={styles.container}>
<ScrollView>
<View style={{styles.newView}}><Text>Test</Text></View>
<View style={{styles.newView}}><Text>Test</Text></View>
<View style={{styles.newView}}><Text>Test</Text></View>
<View style={{styles.newView}}><Text>Test</Text></View>
<View style={{styles.newView}}><Text>Test</Text></View>
<View style={{styles.newView}}><Text>Test</Text></View>
<View style={{styles.newView}}><Text>Test</Text></View>
<View style={{styles.newView}}><Text>Test</Text></View>
</ScrollView>
</View>
)
}
}
const styles = StyleSheet.create({
container:{
flex:1, padding:5
},
newView:{
height: 200, backgroundColor:'green', margin:10
}
})
export default ScrollTest;
I checked this link for Animated API but not able to figureout how to implement it in onScoll?
So the header HomeScreen and the tabs Tab1 and Tab2 should hide on scroll and show when scrolled up. How do I do that?
Please help getting started on this.
Many thanks.
I was also stuck with the same animation thing, I tried this code for maximizing and minimizing the header using the Animated API of react-native.
You can also do the same for showing and hiding it.
import React, { Component } from 'react';
import { Text, View, StyleSheet, ScrollView, Image,Animated } from 'react-native';
const HEADER_MAX_HEIGHT = 200;// set the initial height
const HEADER_MIN_HEIGHT = 60;// set the height on scroll
const HEADER_SCROLL_DISTANCE = HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT;
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
scrollY: new Animated.Value(0),
};
}
render() {
const headerHeight = this.state.scrollY.interpolate({
inputRange: [0, HEADER_SCROLL_DISTANCE],
outputRange: [HEADER_MAX_HEIGHT,HEADER_MIN_HEIGHT],
extrapolate: 'clamp',
});
return(
<View style={{flex: 1}}>
<ScrollView
scrollEventThrottle={1}
onScroll={Animated.event(
[{nativeEvent:
{contentOffset: {y: this.state.scrollY}}}]
)}>
<View style={styles.container}>
<Text style={styles.paragraph}>hello</Text>
<Image source={{uri: "https://static.pexels.com/photos/67843/splashing-splash-aqua-water-67843.jpeg"}} style={styles.imageStyle}/>
<Image source={{uri: "https://www.elastic.co/assets/bltada7771f270d08f6/enhanced-buzz-1492-1379411828-15.jpg" }}
style={styles.imageStyle}/>
</View>
</ScrollView>
<Animated.View style={[styles.footer, {height: headerHeight}]}>
<View style={styles.bar}>
<Text>text here</Text>
</View>
</Animated.View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 24,
backgroundColor: '#ecf0f1',
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
color: '#34495e',
},
imageStyle: {
height: 360,
width: '100%',
},
footer: {
position:'absolute',
top: 0,
left: 0,
right: 0,
backgroundColor: 'red',
},
bar: {
alignItems: 'center',
justifyContent: 'center',
},
});
Hope this helps someone.
I resolved for my case, hope this will be helpful
import React from 'react';
import {
Animated,
Text,
View,
StyleSheet,
ScrollView,
Dimensions,
RefreshControl,
} from 'react-native';
import Constants from 'expo-constants';
import randomColor from 'randomcolor';
const HEADER_HEIGHT = 44 + Constants.statusBarHeight;
const BOX_SIZE = Dimensions.get('window').width / 2 - 12;
const wait = (timeout: number) => {
return new Promise((resolve) => {
setTimeout(resolve, timeout);
});
};
function App() {
const [refreshing, setRefreshing] = React.useState(false);
const scrollAnim = new Animated.Value(0);
const minScroll = 100;
const clampedScrollY = scrollAnim.interpolate({
inputRange: [minScroll, minScroll + 1],
outputRange: [0, 1],
extrapolateLeft: 'clamp',
});
const minusScrollY = Animated.multiply(clampedScrollY, -1);
const translateY = Animated.diffClamp(minusScrollY, -HEADER_HEIGHT, 0);
const onRefresh = React.useCallback(() => {
setRefreshing(true);
wait(2000).then(() => {
setRefreshing(false);
});
}, []);
return (
<View style={styles.container}>
<Animated.ScrollView
contentContainerStyle={styles.gallery}
scrollEventThrottle={1}
bounces={true}
showsVerticalScrollIndicator={false}
style={{
zIndex: 0,
height: '100%',
elevation: -1,
}}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollAnim } } }],
{ useNativeDriver: true }
)}
overScrollMode="never"
contentInset={{ top: HEADER_HEIGHT }}
contentOffset={{ y: -HEADER_HEIGHT }}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}>
{Array.from({ length: 20 }, (_, i) => i).map((uri) => (
<View style={[styles.box, { backgroundColor: 'grey' }]} />
))}
</Animated.ScrollView>
<Animated.View style={[styles.header, { transform: [{ translateY }] }]}>
<Text style={styles.title}>Header</Text>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
gallery: {
flexDirection: 'row',
flexWrap: 'wrap',
padding: 4,
},
box: {
height: BOX_SIZE,
width: BOX_SIZE,
margin: 4,
},
header: {
flex: 1,
height: HEADER_HEIGHT,
paddingTop: Constants.statusBarHeight,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
top: 0,
left: 0,
right: 0,
backgroundColor: randomColor(),
},
title: {
fontSize: 16,
},
});
export default App;
checkout on Expo https://snack.expo.io/#raksa/auto-hiding-header