Layout trouble in React Native - react-native

I'm having a terrible time with layout in one spot. I'd like the View with the buttons to get a consistent amount of space, and then the rest of the space goes to the box with ingredient names on the left.
Here's a screenshot, using Expo Go on iPhone.
Screenshot with problem layout.
I wanted the left box (nameView, red outline) to take all the space not needed by the buttons, but I'm failing miserably. The top row is too small, the second row is too wide and is pushing the buttons off the screen!
Here's the (slightly simplified) return:
<View style={styles.rowView} key={oneitem\[0\]}>
<TouchableOpacity >
<View style={\[styles.nameView, { backgroundColor: oneitem\[1\].sku?'white': 'red'}\]}>
<Text style={styles.itemtext}>{oneitem\[0\]}</Text> <Text>{oneitem\[1\].sku}</Text>
</View>
</TouchableOpacity>
<View style={styles.buttonView}>
<Button style={styles.smallButton} >
<Image source={Images.trash} style={styles.trashButton}/></Button>
<Button style={styles.smallButton} ><Text style={styles.smallbuttonText}>-</Text></Button>
<Text style={styles.quantitytext}>{oneitem\[1\].quantity}</Text>
<Button style={styles.smallButton} > <Text style={styles.smallbuttonText}>+</Text></Button>
</View>
</View>
And here's the relevant styling:
rowView: {
marginBottom: 4,
flexDirection: 'row',
borderWidth: 2,
borderColor: 'yellow',
width: '100%',
maxWidth: '100%',
flex: 1 ,
justifyContent: 'space-between'
},
nameView: {
flex: 1,
flexDirection: 'column',
alignSelf: 'center',
marginLeft: 4,
borderWidth: 1,
borderColor: 'red',
flexGrow: 2,
},
buttonView: {
flexDirection: 'row',
alignContent: 'space-around',
alignItems: 'center',
borderWidth: 1,
width: 125,
flexShrink: 125,
},
UPDATE: I found my issue. I needed to apply the nameView styling to the TouchableOpacity. Next problem: how to get the rows to expand vertically when the text wraps.
Second attempt

The left view should have flex: 1 style! and the left view should have fix width

I found my issue - I needed to apply the nameView style to the TouchableOpacity also. That gets the text to wrap, although now the rows are too short.

Related

React-native: Layout a view next to a centered one

I want to achieve the following:
I want to display a label next to a centered value.
As far as I can see that's not possible with flexbox, right?
How could I achieve that?
Additionally I'd like to have the base-aligned, but currently this option is not working on Android. Therefore I'm using hardcoded paddings for the smaller text boxes. Any other Idea for that?
Another suggestion, if you don't want to use empty Views, is to position Value to the center, and then have the unit positioned absolute (so it goes off the flex) and to the bottom so you ensure it's base-aligned to Value.
<View style={styles.container}>
<Text style={styles.value}>
Value
<Text style={styles.unit}>unit</Text>
</Text>
</View>
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: 'lightgrey',
},
value: {
backgroundColor: '#eee',
textAlign: 'center',
fontSize: 20,
position: 'relative',
},
unit: {
fontSize: 12,
position: 'absolute',
bottom: 0,
},
});
You can use use 3 flex boxes placed horizontally to get a similar effect. Leave the left box blank.
(Ive added background colors for reference)
<View style={styles.container}>
<View
style={{
flexDirection: 'row',
}}>
<View style={{ flex: 1}} /> {/* blank view */}
<View
style={{
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
}}>
<Text style={{ fontSize: 30 }}>Value</Text>
</View>
<View
style={{
flex: 1,
justifyContent: 'flex-end',
alignItems: 'fllex-start',
}}>
<Text style={{ fontSize: 12 }}>Unit</Text>
</View>
</View>
</View>

How to center react native headerTitle *component* (not string)

For the life of me, I can't center the headerTitle component. Ideas?
class GroupSelectionScreen extends React.Component{
static navigationOptions = ({navigation}) => {
return {
headerLeft: <View>
<Image source={logoImg} style={{width:72, height:33, marginLeft:5}}/>
</View>,
headerStyle: {
backgroundColor: theme.colors.darkBlue,
height: 75,
},
headerTitle: <View style={{
alignItems: "center",
flexDirection: 'row',
}}>
<View style={{backgroundColor: statusColor, width: 15, height: 15, borderRadius: 8}}></View>
<Text style={{color: 'white', fontSize: 25}}>{username}</Text>
</View>,
headerRight: <SetStatusButton onPress={navigation.getParam('toggleSetStatus')}/>,
};
};
Because you are using flexDirection:'row' you will probably want to add the property justifyContent:'center' to center the content of the view horizontally
This is due to the fact that justifyContent works on the primary axis and alignItems work on the secondary axis. Setting the flexDirection as row changes the primary axis to be row.
Adding flexDirection to a component's style determines the primary axis of its layout.
Adding justifyContent to a component's style determines the distribution of children along the primary axis.
You can see more in the docs
https://facebook.github.io/react-native/docs/flexbox
Here is a snack https://snack.expo.io/#andypandy/flexdirection
Each of the three views have flexDirection: 'row'
The first view has justifyContent: 'center' and the text is centred horizontally.
The second view has alignItems: 'center' and the text is centred vertically.
The third view has both justifyContent: 'center' and alignItems: 'center' and the text is centred horizontally and vertically
Here is an image of what it looks like
And here is the code
<View>
<View style={{flexDirection: 'row', justifyContent: 'center', backgroundColor: 'yellow', height: 100, width: 100, margin: 10}} >
<Text>Hello</Text>
</View>
<View style={{flexDirection: 'row', alignItems: 'center', backgroundColor: 'cyan', height: 100, width: 100, margin: 10}} >
<Text>Hello</Text>
</View>
<View style={{flexDirection: 'row', justifyContent: 'center', alignItems:'center', backgroundColor: 'forestgreen', height: 100, width: 100, margin: 10}} >
<Text>Hello</Text>
</View>
</View>
Update
In react-navigation there appears to be a difference between how headerTitle given by the question poster is handled in Android and iOS.
In iOS the headerTitle centres vertically and horizontally, however in Android it centres only vertically and is aligned left. Obviously this is not ideal.
There is a way to make it work in both iOS and Android, simply
Wrap the original header in a View with flex:1
Make sure the original header's View has a style with justifyContent: 'center' and alignItems: 'center'
Here is the code for the new headerTitle
<View style={{flex: 1}}>
<View style={{flexDirection: 'row', justifyContent: 'center', alignItems: 'center'}}>
<View style={{backgroundColor: 'green', width: 15, height: 15, borderRadius: 8}}/>
<Text>Spencer</Text>
</View>
</View>
Here is a snack showing it working https://snack.expo.io/#andypandy/navigation-header

Horizontal ScrollView with content on two lines

I would like to render an horizontal scroll view with two rows of items, where the items have dynamic sizes. I have already tried to set a height to the scroll view and apply to its containerStyle flex-wrap with flex direction column, as suggested here React Native horizontal FlatList with multiple rows.
However, the result is not as expected since two elements in a column take the same size.
<View style={{height: 80}}>
<ScrollView
horizontal={true}
containerStyle={{
flexDirection: "column",
flexWrap: "wrap",
alignItems: "flex-start"
}}
>
{data.map(d => renderElement(d))}
</ScrollView>
</View
renderElement = (d: string) => (
<View
style={{
justifyContent: "center",
alignItems: "center",
borderWidth: 1,
borderColor: "#898989",
backgroundColor: "white",
borderRadius: 12,
margin: 3,
height: 24
}}
>
<Text style={{marginHorizontal: 8, fontSize: 16, color:"#898989#"}}>
{d}
</Text>
</View>
)
current result:

How to justify (left, right, center) each child independently?

In react native I have:
<View style={styles.navBar}>
<Text>{'<'}</Text>
<Text style={styles.navBarTitle}>
Fitness & Nutrition Tracking
</Text>
<Image source={icon} style={styles.icon}/>
</View>
with these styles:
{
navBar: {
height: 60,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
navBarTitle: {
textAlign: 'center',
},
icon: {
height: 60,
resizeMode: 'contain',
},
}
This is the effect I get:
This is the effect I want:
In the first example, the spacing between items is equal.
In the second example, each item is justified differently. The first item is left-justified. The second item is center-justified. The third, right-justified.
This question is similar, but it looks like react native does not support margin: 'auto'. Furthermore, the other answers only work if you only care about left and right justification, but no one really addresses center justification without auto margin.
I am trying to make a navigation bar in react native. The vanilla ios version looks like this:
(source: apple.com)
How do I do something similar? I'm mainly concerned with centering.
One way is to use nested View (flex containers) for 3 different regions and set flex:1 to left and right region
<View style={styles.navBar}>
<View style={styles.leftContainer}>
<Text style={[styles.text, {textAlign: 'left'}]}>
{'<'}
</Text>
</View>
<Text style={styles.text}>
Fitness & Nutrition Tracking
</Text>
<View style={styles.rightContainer}>
<View style={styles.rightIcon}/>
</View>
</View>
const styles = StyleSheet.create({
navBar: {
height: 60,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: 'blue',
},
leftContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-start',
backgroundColor: 'green'
},
rightContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
backgroundColor: 'red',
},
rightIcon: {
height: 10,
width: 10,
resizeMode: 'contain',
backgroundColor: 'white',
}
});
You could also set marginLeft: 'auto' to the middle component. It should push it to the right. Also works for React Native
Source: https://hackernoon.com/flexbox-s-best-kept-secret-bd3d892826b6
If you're using a NavigationBar from the Navigator module see my question: Changing the default style for a Navigator.NavigationBar (title)
use this
marginLeft: "auto",
marginRight: "auto"

Style width using flex in React Native

I'm working in react native and I'm trying to style a component. I have a view (black border) that wraps two views (A and B). I would like something that looks like this - where I the view that wraps A has a width of about 75% and the view that wraps B has a width of about 25%.
When this component is rendered out as a list, however, what I'm getting is something like this - where the width of the A view is dependent on how much text it holds.
I was able to set the width to a specific amount, but it always seems to change based on the device I test it on (different on iPhone 5, 6, 6 plus, etc.). Therefore I'm trying to use flex styling, but can't seem to get it right. Here is my code for my styling and component structure:
const styles = StyleSheet.create({
title: {
color: '#000',
textAlign: 'left',
fontSize: 80,
},
row: {
flex: 1,
flexDirection: 'row',
padding: 0,
height: 180,
borderBottomColor: '#888',
borderBottomWidth: 1,
alignItems: 'stretch'
},
innerContainer: {
flex: 1,
padding: 0,
flexDirection: 'row',
alignItems: 'stretch',
},
sideDivider: {
flex: 0.8,
padding: 0,
backgroundColor: '#cacaca',
justifyContent: 'center'
},
sideDividerArrow: {
flex: 0.2,
padding: 0,
alignItems: 'center',
backgroundColor: 'cyan',
justifyContent: 'center',
},
});
...
render() {
return (
<View style={styles.row} ref={component => this._root = component}>
<TouchableHighlight onPress={this.handleClick.bind(this)}>
<View style={styles.innerContainer}>
<View style={styles.sideDivider}>
<Text style={styles.title}>
{this.state.routeData.busNumber}
</Text>
<Text>{this.state.routeData.fullRoute}</Text>
</View>
<View style={styles.sideDividerArrow}>
<Text>></Text>
</View>
</View>
</TouchableHighlight>
</View>
)
}
}
Any and all help is appreciated. Thanks!!
The flex attribute expects an integer. We cannot use fractions for it.
I guess rendering the following will help you achieve the desired look. You can then add your interactions and clean up the inline styles
<View style ={{flexDirection:'row',flex:1,borderColor:'black',borderWidth:1}}>
<View style ={{flex:3,backgroundColor:'blue',alignItems:'center',justifyContent:'center'}}>
<Text>A</Text>
</View>
<View style ={{flex:1,backgroundColor:'yellow',alignItems:'center',justifyContent:'center'}}>
<Text>B</Text>
</View>
</View>
Your picture has 3 components, but the code actually has more.
The code should work as intended if you do:
- row: flex: 1 (to make sure row is full width) + flexDirection: 'row' (so row children will be left to right).
- A container: flex: 3.
- B container: flex: 1.
At least one of the children of A or B should have a flex: 1 (or any other number) to ensure that children also stretch to fill the parent.
In your case, giving the title and the text components a flex: 1 property will probably do the trick.
Hope this helps!