Skip to content

Instantly share code, notes, and snippets.

@kmagiera
Created April 21, 2017 08:10
Show Gist options
  • Save kmagiera/710460dac3ba722bb6a8d552c343b44a to your computer and use it in GitHub Desktop.
Save kmagiera/710460dac3ba722bb6a8d552c343b44a to your computer and use it in GitHub Desktop.
import React, { Component } from 'react'
import { View, Image, StyleSheet, ScrollView, Text, Animated, StatusBar, PixelRatio } from 'react-native'
import Icon from 'react-native-vector-icons/MaterialIcons';
import MapView from 'react-native-maps';
const AMSTERDAM = {
latitude: 52.3702,
longitude: 4.8952,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}
const OFFSET = 450
class Maps extends Component {
render() {
return (
<View style={{flex: 1}}>
<MapView style={{flex: 1}} initialRegion={AMSTERDAM}/>
<Drawer />
</View>
)
}
}
class Drawer extends Component {
constructor(props) {
super(props)
this.state = {
expanded: false,
}
}
handleEndDrag = (e) => {
const targetY = e.nativeEvent.targetContentOffset.y
if (targetY < OFFSET) {
const snap = targetY < OFFSET / 3 ? 0 : OFFSET
this.refs["Scroll"].scrollTo({ y: snap })
}
}
handleBeginDrag = (e) => {
this.setState({ expanded: true })
}
handleAnimationEnd = (e) => {
const scrollY = e.nativeEvent.contentOffset.y
this.setState({ expanded: scrollY > 0 })
}
render() {
return (
<View style={[styles.container, { overflow: this.state.expanded ? 'visible' : 'hidden' }]}>
<ScrollView
style={styles.full}
stickyHeaderIndices={[1]}
showsVerticalScrollIndicator={false}
contentInset={{bottom: -255}}
ref="Scroll"
onScrollEndDrag={this.handleEndDrag}
onScrollBeginDrag={this.handleBeginDrag}
onMomentumScrollEnd={this.handleAnimationEnd}
>
<View style={styles.placeholder} />
<View style={styles.header}>
<View style={styles.handle} />
<Text style={styles.headerText}>Sticky header (pull here)</Text>
</View>
<Row icon="event-seat" text="Go watch a movie" color="#3949ab"/>
<Row icon="euro-symbol" text="Find some ca$$h" color="#00695c"/>
<Row icon="rowing" text="Random guy with a paddle" color="#827717"/>
<Row icon="radio" text="Very old radio" color="#6d4c41"/>
<Row icon="airplanemode-active" text="Fly away" color="#424242"/>
<Row icon="usb" text="Buy some USBs" color="#546e7a"/>
<Row icon="explore" text="Maybe use internet explorer" color="#e53935"/>
<Row icon="watch" text="Charge your watch..." color="#ff8f00"/>
<Row icon="wb-sunny" text="Good weather!" color="#1b5e20"/>
<Row icon="local-hospital" text="Need help?" color="#ad1457"/>
<Row icon="restaurant" text="Some fancy restaurant" color="#6a1b9a"/>
<Row icon="directions-bike" text="Get on a bike" color="#c0ca33"/>
<View style={styles.white}/>
</ScrollView>
</View>
)
}
}
const Row = ({icon, text, color}) => (
<View style={styles.row}>
<View style={[styles.rowIconContainer, {backgroundColor: color}]}>
<Icon name={icon} size={20} color="white" />
</View>
<Text style={styles.rowText}>{text}</Text>
</View>
)
const styles = StyleSheet.create({
container: {
position: 'absolute',
left: 0,
right: 0,
top: 40 + OFFSET,
bottom: 0,
},
full: {
marginTop: -OFFSET,
flex: 1,
},
header: {
backgroundColor: 'white',
height: 62,
borderTopWidth: 1 / PixelRatio.get(),
borderBottomWidth: 1 / PixelRatio.get(),
borderColor: '#8C99A5',
alignItems: 'center',
},
handle: {
width: 40,
height: 8,
borderRadius: 4,
backgroundColor: '#8C99A5',
margin: 4,
},
placeholder: {
height: OFFSET,
},
headerText: {
flex: 1,
fontSize: 22,
},
row: {
height: 60,
flex: 1,
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'white',
borderBottomWidth: 1 / PixelRatio.get(),
borderColor: '#8C99A5',
},
rowIconContainer: {
margin: 10,
width: 30,
height: 30,
borderRadius: 15,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
},
rowText: {
fontSize: 18,
},
white: {
height: 255,
backgroundColor: 'white',
},
})
export default Maps
@ferrannp
Copy link

Hey @kmagiera! First of all, great talk at React Amsterdam and thanks for this snippet! I am trying to implement the Material Bottom Sheets with this example. I need to figure out a couple of things: Content behind to be clickable + add and overlay that changes opacity while the sheet is moving.

I am writing this because I tested the snippet and every time I end dragging, I receive an exception:

undefined is not an object (evaluating ‘e.nativeEvent.targetContentOffset.y’)

I didn't really check why is this happening but a preventive check seems to work:

 if (!e.nativeEvent.targetContentOffset) {
  return;
}

I use react-native: 0.43.4 and react: 16.0.0-alpha.6.

@kmagiera
Copy link
Author

kmagiera commented May 9, 2017

@ferrannp is this on iOS or android? I think the targetContentOffset may not be available on android in which case you may need to find another workaround.

As for the content behind being clickable this example should provide that. It works by changing the container size in which the scollview is placed (this.state.expanded is responsible for that)

Sorry for late response one again

@ferrannp
Copy link

@kmagiera yup Android, also the content behind is clickable on iOS but not on Android... So it looks like that getting this on Android https://material.io/guidelines/components/bottom-sheets.html#bottom-sheets-usage it is very tricky...

@j1mie
Copy link

j1mie commented Jul 29, 2018

Hey @kmagiera - running into a similar issue with Android as above. Any pointers would be much appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment