React Native Maps with sticky and multiple Callouts - Same as Uber app - react-native

I would like to have multiple callout which are alway open in the same way as Uber taxi app does.
The criteria I want are
Multiple Callouts
Callout can be pressed
I have two solutions. One is to always set callouts open by reference once it is mounted, but it comes with only one active callout at once. Secondly, I use custom Marker with absolute position and TouchableOpacity as child. I can display multiple, but the button nested in <MapView.Marker> cannot be pressed.

I was struggling with the same problem for sometime. Actually this is how I stumbled across this question. I wanted to share what I ended up with. Apparently we can place anything inside the Marker component, so putting a View and an Image inside did the trick:
<Marker coordinate={coords} style={styl.customMarker}>
<View style={styl.addressBox}>
<TouchableOpacity onPress={() => console.log('Click!')}>
<Text numberOfLines={2} style={styl.addressText}>
Some location address goes here
</Text>
</TouchableOpacity>
</View>
<Image source={require('../assets/locationPin.png')} />
</Marker>
const styl = StyleSheet.create({
customMarker: {
justifyContent: 'center',
alignItems: 'center'
},
addressBox: {
width: 150,
height: 45,
backgroundColor: '#fff',
paddingVertical: 5,
paddingHorizontal: 7,
borderRadius: 5,
borderWidth: 0.5,
borderColor: '#ccc',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.3,
shadowRadius: 5,
elevation: 3
},
addressText: {
textDecorationLine: 'underline'
}
});
This is how it looks:
This is a Grab (Uber alternative) implementation. But you can play around with View and Image styles to achieve Uber like results.
Hope this helps someone in the future. Cheers!

Related

How to style a component to hide behind another component

I'm quite new to react native, I'm trying to understand how I can style two components to intercept with each other. Desirably have one component move behind the other component as if they were layers in Photoshop. I'd like to achieve this so that I can animate the styling of the hidden component to reveal it self on press of a touchable opacity. But i'm fine with the animation, I don't need any help there. I only need direction with how to correctly style a component to hide behind another component.
Here are the two components I'm working with, a text component contained inside a touchable opacity (the text box), and a date of publish text component. Both of which are contained inside a main view component.
<View style={styles.contentContainer}>
<View style={styles.dateContainer}>
<Text style={styles.dateText}>a few seconds ago</Text>
</View>
<TouchableOpacity onPress={() => {}}>
<Text style={styles.item}>this is some text input</Text>
</TouchableOpacity>
</View>
const styles = StyleSheet.create({
contentContainer: {
flex: 1,
},
item: {
padding: 16,
fontWeight: 'bold',
borderColor: '#bbb',
borderWidth: 1,
borderStyle: 'solid',
borderRadius: 10,
},
dateContainer: {
flex: 1,
paddingHorizontal: 2,
marginTop: 16,
alignSelf: 'flex-end',
},
dateText: {
color: '#adadad',
},
});
You can use the position:'absolute' and the zIndex in styles to have the layers for views.
The below style would place the dateContainer above the text component and in the corner of the text compoment. You can position the view using left,right,top or bottom.
dateContainer: {
position: 'absolute',
flex: 1,
paddingHorizontal: 2,
top: 30,
alignSelf: 'flex-end',
zIndex: 1000,
},

How to position a button in bottom right corner of MapView in React Native?

I'm trying to wrap my head around flexbox, and also how the component tree works with a react-native-maps MapView (and whether that's different from other components with components "on top of them" for which I'm currently using Callout.
For example, here's my MapView's render method. The TouchableOpacity area (and its buttonCallout container. is currently just for my experiments in understanding layout. I'm using the F8StyleSheet wrapper from makeitopen.com
render() {
return (
<View style={styles.container}>
<MapView
style={{ flex: 1 }}
region={this.state.region}
showsUserLocation={true}
>
{this.renderMarkers()}
</MapView>
<Callout style={styles.searchCallout}>
<TextInput
onChangeText={searchText => this.setState({ searchText })}
onSubmitEditing={this.handleSearch.bind(this)}
style={styles.calloutSearch}
placeholder={"Search"}
value={this.state.searchText}
/>
</Callout>
<Callout style={styles.buttonCallout}>
<TouchableOpacity
style={[styles.touchable]}
onPress={() => console.log("press")}
>
<Text style={styles.touchableText}>Press Me 1</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.touchable]}
onPress={() => console.log("press")}
>
<Text style={styles.touchableText}>Press Me 2</Text>
</TouchableOpacity>
</Callout>
</View>
);
}
}
const styles = F8StyleSheet.create({
container: {
flex: 1
},
buttonCallout: {
flex: 1,
alignSelf: "flex-end",
justifyContent: "space-between",
backgroundColor: "transparent",
borderWidth: 0.5,
ios: { padding: 5 },
borderRadius: 20
},
touchable: {
backgroundColor: "lightblue",
padding: 10,
margin: 10
},
touchableText: {
fontSize: 24
},
searchCallout: {
flexDirection: "row",
backgroundColor: "rgba(255, 255, 255, 0.9)",
borderRadius: 10,
width: "80%",
marginLeft: "5%",
marginTop: 40
},
calloutSearch: {
borderColor: "transparent",
marginLeft: 10,
width: "90%",
marginRight: 10,
height: 40,
borderWidth: 0.0
}
});
It makes sense how I use margins to get the searchCallout to be where I want it to be because it's at the top.
As the code is now, the buttonCallout (with the TouchableOpacity inside) renders in the top right corner (sensible - alignSelf: 'flex-end' puts it at the "end", on the right).
...
Mid-post update! By changing the styles.container's flexDirection to row, with the buttons callout at alignSelf: flex-end. I've got the button callout on the bottom, and the search callout is still on top. I guess Callout components all render on top of each other.
So now I can use justifyContent on styles.container to have both callouts either in the center or on the left or right (center, flex-start, flex-end). How do I justify the different items separately? (justifySelf is not a thing!)
Wrapping both callouts in a unstyled View component results in the button callout rendering at the top-right but the search callout being nowhere to be found.
Wrapping them both in a callout results in the buttons rendering a little right of the top left corner, with the search callout again not displayed.
Help me understand all this! Thanks :)
if you're trying to position the buttonCallout at the bottom, give it a postion:'absolute', so it will stay at the bottom
buttonCallout: {
flex: 1,
flexDirection:'row',
position:'absolute',
bottom:10,
alignSelf: "center",
justifyContent: "space-between",
backgroundColor: "transparent",
borderWidth: 0.5,
borderRadius: 20
},

How to apply shadow to react native View without affecting inner view elements?

I would like to know how to apply shadow only to the main outer view. Here on applying shadow, it's getting applied to all the inner elements
The trick to make the shadow props of parent don't inherit to children element, is to set a background color to the component on which you set the shadow. For example that would be:
<View
style={{ backgroundColor: '#fff' }}
shadowOffset={{height: 10}}
shadowColor='black'
shadowOpacity={0.5}
>
<Text>{title}</Text>
</View>
Unfortunately this only works with colored backgrounds – when setting a transparent background with RGBA or 'transparent' is doesn't help.
I cannot really answer based on a simple image, but from my previous experience, setting shadow offset to the required height and width should do the trick for the iOS.
Read more about it here: Shadow Offset
Here's a picture of what my card looks like with the following style used:
marginLeft: 10,
backgroundColor: 'white',
shadowColor: 'blue',
alignItems: 'center',
shadowOffset: {width: 3, height: 3 },
shadowOpacity: 1.0,
borderRadius: 10,
My card View
Hope it works out well for you.
Display custom shadow color >= 28 or >= P for above Sdk level 28
Code
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<View
style={{
shadowColor: 'purple',
height: 150,
width: '90%',
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
//android specific
elevation: 10,
//ios specific
shadowOffset: { width: 1, height: 1 },
shadowRadius: 3,
shadowOpacity: 0.5,
}}>
<Text style={{ color: 'rgba(128,0,128,0.5)' }}>
Welcome to React Native
</Text>
</View>
</View>
In android we can adjust shadow by elevation property
In iOS we can adjust shadow by shadowOffset, shadowRadius,shadowOpacity property
Output android
Output iOS
Available library for further usage
react-native-shadow-2
react-native-drop-shadow
Create a Shadow.js
export const Shadow = (elevation) => { // Receive elevation as a prop
return {
elevation,
shadowColor: 'black',
shadowOffset: { width: 0, height: 0.5 * elevation },
shadowOpacity: 0.3,
shadowRadius: 0.8 * elevation,
};
};
import Shadow.js to the page where you want to apply
import Shadow from './Shadow' //path to Shadow.js
<View style={{Shadow(5)}}> // pass elevation as a prop to Shadow.js
</View>
if you want to use in styles
import Shadow from './Shadow' //path to Shadow.js
<View style={styles.shadow}>
</View>
const styles = StyleSheet.create({
shadow:{
...Shadow(5) //use spread operator
}
});

Create "Raised" or Shadow effect on TouchableOpacity: React Native

I use react-native-elements, and they make it easy to make an actual button appear "raised", or shadowed. I want to duplicate this look for TouchableOpacity.
Here's an example of the "raised buttons" with react-native-elements.
It is subtle, but noticeably makes a nice impact.
How would you go about mimicking this effect with TouchableOpacity?
And yes, I've gone through the docs. If I missed anything, please point it out... but I don't think anything is there to accomplish this. Thanks.
You can add the following styles to your TouchableOpacity to make it raised.
<TouchableOpacity style={styles.button} />
button: {
shadowColor: 'rgba(0,0,0, .4)', // IOS
shadowOffset: { height: 1, width: 1 }, // IOS
shadowOpacity: 1, // IOS
shadowRadius: 1, //IOS
backgroundColor: '#fff',
elevation: 2, // Android
height: 50,
width: 100,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
},
You can read more about them here.
IOS Specific Props
Android Specific Props

TouchOpacity does not cover whole menu link

I just started using react-native so sorry if it's a trivial question.
I'm trying to create a sidebar with multiple links in it. The sidebar works fine but the issue is with the links themselves. Below is the jsx for the link.
<TouchableOpacity style={MenuItemStyles.ItemWrapper} onPress={this.props.onPress}>
<View style={MenuItemStyles.itemIcon}>
<Icon
name={this.props.iconName}
size={this.props.size || 30}
color={Colours.LightGrey}
/>
</View>
<Text style={MenuItemStyles.itemLabel}>
{this.props.label}
</Text>
</TouchableOpacity>
And the style:
const MenuItemStyles = StyleSheet.create({
ItemWrapper: {
flexDirection: 'row',
alignSelf: 'stretch',
marginTop: 10,
width: 100,
marginBottom: 10
},
itemIcon: {
alignItems: 'center',
alignSelf: 'center',
width: 80,
},
itemLabel: {
color: '#000000',
fontSize: 20,
fontFamily: 'sans-serif-light',
textAlign: 'center',
marginLeft: 5,
}
});
The link contains an icon(Material style), follows by the label. The onPress event is registered correctly. However the click area size is very small ie onPress only triggers when pressing on the icon, not the label. I would assume TouchableOpacity covers all nested component?Can I control how wide TouchableOpacity cover?
Thanks
Wrap your <TouchableOpacity/>component in view that has the style you are curenttly assigning on TouchableOpacity like this <View style={MenuItemStyles.ItemWrapper}>
and by adding flex:1 on the <TouchableOpacity/> component it will inherit the size of the <View>
heres a working example of what I think you are trying to accomplish with the solution above implemented:
https://rnplay.org/apps/SW983Q