I have a flatlist component that is 2 rows but the columns don't distribute properly. I'm having trouble styling it to achieve this.
My Flatlist:
<FlatList
contentContainerStyle={styles.list}
numColumns={2}
data={this.props.route.params.data}
keyExtractor={(item, index) => item.id}
renderItem={({item}) => (
<TouchableOpacity
style={styles.view}
onPress={() =>
item.subcategories
? this.props.navigation.navigate('Browse', {
screen: 'Subcategories',
params: {data: item.subcategories},
})
: this.props.navigation.navigate('Browse', {
screen: 'Products',
params: {id: item.id},
})
}
underlayColor="grey">
<View style={styles.view}>
<Text style={styles.title}>{item.title}</Text>
</View>
</TouchableOpacity>
)}
/>
Style:
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
list: {
flexDirection: 'column',
},
view: {
backgroundColor: 'grey',
alignItems: 'center',
justifyContent: 'center',
alignContent: 'stretch',
margin: 1,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 2,
},
title: {
textAlign: 'center',
fontSize: 20,
padding: 5,
width: '100%',
},
});
What it looks like:
I know I can set the width to a number, but I want it to dynamically change with screen size.
Can you try add width: '50%' to the view style or width: Dimensions.get('window').width / 2
Related
Does anyone know how to render a slider in react-native like the below, with distinct sections? So far, have only seen sliders with one line and one dot.
You can achieve this type of slider by using LayoutAnimation. Find the below code to get it done.
import React, { useState } from 'react';
import {Button,LayoutAnimation,Platform,StyleSheet,Text,UIManager,View} from 'react-native';
if (Platform.OS === 'android') {
if (UIManager.setLayoutAnimationEnabledExperimental) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
const step = ['1', '2', '3', '4', '5', '6', '7'];
const activeColor = '#444';
export default function TrackingStatus() {
const [activeIndex, setActive] = useState(0);
const setActiveIndex = (val) => {
LayoutAnimation.easeInEaseOut();
setActive(val);
};
const marginLeft = (100 / (step.length - 1)) * activeIndex - 100 + '%';
return (
<View style={styles.constainer}>
<Text style={styles.prop}>{activeIndex}</Text>
<View style={styles.statusContainer}>
<View style={styles.line}>
<View style={[styles.activeLine, { marginLeft }]} />
</View>
{step.map((step, index) => (
<View style={[styles.dot]} key={step}>
<View
style={[
index <= activeIndex
? { height: '100%', width: '100%' }
: { height: '0%', width: '0%' },
{ backgroundColor: activeColor, borderRadius: 20 },
]}
/>
</View>
))}
</View>
<View style={styles.btns}>
<Button
title="prev"
onPress={() => setActiveIndex(activeIndex - 1)}
disabled={activeIndex <= 0}
/>
<Button
title="next"
onPress={() => setActiveIndex(activeIndex + 1)}
disabled={activeIndex >= step.length - 1}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
constainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 30,
},
statusContainer: {
flexDirection: 'row',
alignItems: 'center',
width: '100%',
height: 70,
justifyContent: 'space-between',
},
dot: {
height: 15,
width: 15,
borderRadius: 10,
backgroundColor: '#ccc',
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
},
line: {
height: 5,
width: '100%',
backgroundColor: '#ccc',
position: 'absolute',
borderRadius: 5,
overflow: 'hidden',
},
activeLine: {
height: '100%',
width: '100%',
backgroundColor: activeColor,
borderRadius: 5,
},
btns: {
width: '100%',
flexDirection: 'row',
justifyContent: 'space-evenly',
marginTop: 20,
},
prop: {
marginBottom: 20,
width: 100,
textAlign: 'center',
},
});
In the above code, We simply create a UI clone as required and add margin-left as well as filling dots with colour. just before changing the state of margin-left & dot colour, we added layout animation to add an animation effect for progress.
Output
I hope this explanation will help you understand the working
I follow a tutorial for creating a sliding tab or segment component using animation like below:
const SlidingTabBar = (props) => {
const [active, setActive] = useState(0)
const [xTabOne, setXTabOne] = useState(0)
const [xTabTwo, setXTabTwo] = useState(0)
const [transX, setTransX] = useState(new Animated.Value(0))
const handleSlide = (type) => {
Animated.spring(transX, {
toValue: type,
duration: 100
}).start()
}
return (
<View style={styles.screen}>
<View style={styles.tabContainer}>
<Animated.View style={styles.active, {transform: [{ translateX: transX}]}} />
<TouchableOpacity onLayout={(e) => setXTabOne(e.nativeEvent.layout.x)} onPress={() => setActive(0), handleSlide(xTabOne)}><Text>tab one</Text></TouchableOpacity>
<TouchableOpacity onLayout={(e) => setXTabTwo(e.nativeEvent.layout.x)} onPress={() => setActive(1), handleSlide(xTabTwo)}><Text>tab 2</Text></TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
tabContainer: {
flexDirection: 'row',
borderWidth: 1,
borderColor: 'blue',
width: 300,
justifyContent: 'space-around',
position: 'relative',
},
active: {
position: 'absolute',
backgroundColor: 'lightblue',
width: '50%',
height: '100%',
left: 0,
top: 0
}
})
but when I implement my transform on the View component my overlay disappear and break like this:
any suggestions?
This is very simple in starting I was also struggling in this but I sort it out
Note: code is for functional component
just do it take a View as button container and inside it take to button like this
<View style={styles.tabGroup}>
<TouchableOpacity
activeOpacity={0.6}
underlayColor="#0000ff"
onPress={()=> handleTab("tab1")}
style={[styles.tabButton, tab.tab1 && styles.tabButtonActive]}
>
<Text style={[styles.tabButtonTitle, tab.tab1 && styles.tabButtonTitleActive]}>Tab btn1</Text>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.6}
underlayColor="#0000ff"
onPress={()=> handleTab("tab2")}
tyle={[styles.tabButton, tab.tab2 && styles.tabButtonActive]}
>
<Text style={[styles.tabButtonTitle, tab.tab2 && styles.tabButtonTitleActive]}>Tab btn2</Text>
</TouchableOpacity>
</View>
Add some styles to button and button group
tabGroup:{
flexDirection: 'row',
justifyContent: 'center',
backgroundColor: 'rgba(11, 93, 30, 0.11)',
borderRadius: 46,
marginHorizontal: 20,
justifyContent: 'space-between'
},
tabButton:{
backgroundColor: 'transparent',
borderRadius: 46,
paddingHorizontal: 20,
paddingVertical: 12,
alignItems: 'center',
},
tabButtonActive:{
backgroundColor: '#0B5D1E',
borderRadius: 46,
paddingHorizontal: 20,
paddingVertical: 12,
alignItems: 'center',
},
tabButtonTitle:{
fontSize: 16,
fontWeight: 'normal',
textAlign: 'center',
color: '#000'
},
tabButtonTitleActive:{
fontSize: 16,
fontWeight: 'normal',
textAlign: 'center',
color: '#fff'
}
Then define function and state on the top of
const [tab, setTab] = useState({
tab1: false,
tab2: true
});
const handleTab = (swap) =>{
if(swap==="tab1"){
setTab({
tab1: true,
tab2: false
})
}
else{
setTab({
tab1: false,
tab2: true
})
}
}
I would like my page to scroll up so that the user can correctly type its email and password. Therefore, I am using KeyboardAvoidingView.
However, when I use it, it shrinks my textinput picture instead of moving up the page. Without the keyboard my app looks like this picture.
My code following for this page :
export default class LoginScreen extends React.Component {
constructor(props) {
super(props)
}
state = {
email: '',
password: ''
}
handleEmail = (text) => {
this.setState({ email: text })
}
handlePassword = (text) => {
this.setState({ password: text })
}
render() {
return (
<View style={styles.container}>
<ImageBackground source={require('../pics/main.png')} style={styles.background}>
<KeyboardAvoidingView behavior="padding" style={{ flex: 1}} >
<Text style={styles.title2}>WELCOME</Text>
<TextInput
style={styles.inputEmail}
placeholder="Enter Email"
onChangeText={this.handleEmail}
/>
<TextInput
style={[styles.inputEmail, styles.inputPassword]}
placeholder="Enter password"
onChangeText={this.handlePassword}
/>
<View style={styles.buttons}>
<TouchableOpacity style={styles.login} onPress={() => this.props.navigation.navigate('MainScreen')} >
<Text style={styles.loginText}>LOGIN</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.login} onPress={() => this.props.navigation.navigate('SignUpScreen')}>
<Text style={styles.loginText}>SIGN UP</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</ImageBackground>
</View>
);
}
}
const styles = StyleSheet.create({
area: {
flex: 1
},
container: {
flex: 1,
},
background: {
flex: 1,
resizeMode: "cover",
width: '100%',
height: '100%'
},
title2: {
paddingTop: 10,
color: color.primary,
fontSize: 25,
alignSelf: 'center',
justifyContent: 'flex-start'
},
inputEmail: {
borderWidth: 2,
borderColor: color.primary,
borderRadius: 10,
width: '50%',
height: '5%',
marginTop: 100,
alignSelf: 'center',
backgroundColor: color.secondary
},
inputPassword: {
marginTop: 10
},
buttons: {
flexDirection: 'row',
justifyContent: 'space-around',
marginTop: 30,
marginLeft: 70,
marginRight: 70,
},
login: {
borderColor: color.primary,
borderRadius: 20,
borderWidth: 2,
width: '40%',
height: '118%',
backgroundColor: color.primary
},
loginText: {
alignSelf: 'center',
marginTop: 5,
marginBottom: 1,
color: 'white'
}
});
Any idea on what I am doing wrong ?
UPDATE : to avoid shrinking put the text input container width and height not as a percentage but as real numbers : inputEmail --> width: 200, height: 35,
Here is a solution with your code, I have fixed this in this snack with your provided code.
Expo Snack with CSS fixes to avoid keyboard
I have a View (containing an Icon and Text, to act as a big checkbox) which is placed above a ScrollView, however, the ScrollView is appearing in front of the View, and also the Text isn't displaying.
I've tried every combination of flex, sometimes ending up with both of them taking up half the screen.
The Icon/Text works fine on other screens.
this shows the view behind the scrollview, rather than neatly above it, with some margin.
this is what it looks like on another screen, fine with margins, etc.
export default StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
marginTop: 20,
marginBottom: 20,
backgroundColor: 'white',
},
scrollview: {
width: '100%',
paddingTop: 10,
paddingBottom: 10,
},
heading: {
fontSize: 36,
textAlign: 'center',
marginLeft: 20,
marginRight: 20,
},
subheading: {
fontSize: 20,
textAlign: 'center',
marginLeft: 20,
marginRight: 20,
},
});```
<View style={ style.container }>
<Text style={ style.heading }>{ this.state.list && this.state.list.name || 'My Gift List' }</Text>
{ this.state.list.date ? <Text style={ [ style.subheading, { marginTop: 5, marginBottom: 10 } ] }>{ formatDate( dateFromIso( this.state.list.date ), "%A %eth %B, %Y" ) }</Text> : null }
<View style={{ flex: 1, flexDirection: 'row', justifyContent: 'center', alignItems: 'center', marginTop: 10, marginBottom: 20 }}>
<Checkbox icon={ this.state.showTicked ? 'check' : null } onPress={ () => this.setState( { showTicked: ! this.state.showTicked } ) } />
<Text style={{ marginLeft: 10 }} onPress={ () => this.setState( { multiple: ! this.state.multiple } ) }>I would like more than one</Text>
</View>
{
this.state.list.items.length > 0 ?
<ScrollView style={ style.scrollview } >
<FlatList style={ { marginTop: 10, marginBottom: 50, width: "100%", borderTopColor: '#ddd', borderTopWidth: 1 } } data={ this.state.list.items } renderItem={ this.renderItem } />
</ScrollView>
: null
}
</View>```
const Checkbox = args => {
const props = Object.assign( {}, args );
props.containerStyle = Object.assign( { width: 40 }, props.containerStyle || {} );
props.buttonStyle = Object.assign( { backgroundColor: 'transparent', borderWidth: 1, borderColor: '#555', height: 40 }, props.buttonStyle || {} );
if ( props.icon )
{
let iconProps = Object.assign( { type: 'font-awesome', name: props.icon , color: "#555555", size: 20 }, props.iconStyle || {} );;
props.icon = <RNEIcon {...iconProps} />
}
return <RNEButton {...props} />
};
I think You just need to change the z index for the scroll view and the regular view in the style sheet
export default StyleSheet.create({
container: {
zIndex: 2,
flex: 1,
alignItems: 'center',
marginTop: 20,
marginBottom: 20,
backgroundColor: 'white',
},
scrollview: {
zIndex: 1,
width: '100%',
paddingTop: 10,
paddingBottom: 10,
},
heading: {
fontSize: 36,
textAlign: 'center',
marginLeft: 20,
marginRight: 20,
},
subheading: {
fontSize: 20,
textAlign: 'center',
marginLeft: 20,
marginRight: 20,
},
});```
My Tag Component, which consist out of rendered texts and a text input component, renders under another component, my button and behaves super weird.
If you look at the gif you can also see, that the tags from the second line onwards are not getting wrapped by my container style as well.
I made a gif, which should make it easier to see :)
http://www.giphy.com/gifs/JmD2VWAXU3S12LQ0Ya
const styles = StyleSheet.create({
textInput: {
flex: 1,
borderRadius: 8,
paddingLeft: 9,
paddingRight: 9,
height: 30,
marginRight: 15,
},
tagContainer: {
flexDirection: 'row',
flexWrap: 'wrap'
},
tags: {
backgroundColor: "#9AA5B1",
borderRadius: 8,
paddingLeft: 9,
paddingRight: 9,
paddingTop: 4,
paddingBottom: 4,
marginRight: 10,
marginBottom: 4,
flexDirection: "row",
},
text: {
color: "white",
},
container: {
backgroundColor: '#F5F7FA',
borderRadius: 8,
paddingLeft: 9,
paddingRight: 9,
height: 30,
marginRight: 15,
flexDirection: "row",
flexWrap: "wrap",
},
});
renderTags1 = (tag, index, deleteTag) => {
return (
<TouchableOpacity
onPress={index => deleteTag(index)}
style={styles.tags}
key={`${tag}-${index}`}
>
<Text style={styles.text}>{tag}</Text>
</TouchableOpacity>
);
};
const InputTags = ({ tags, value, onChangeText, deleteTag }) => {
return (
<View style={styles.container}>
<View style={styles.tagContainer}>
{tags.map((tag, index) => {
return this.renderTags1(tag, index, deleteTag);
})}
</View>
<TextInput
style={styles.textInput}
placeholder="Outdoor"
onChangeText={text => onChangeText({ text })}
value={value}
autoCorrect={false}
multiline={true}
/>
</View>
);
};
export { InputTags };
For my Button :
import React from 'react';
import { Text, TouchableOpacity, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
view: {
backgroundColor: '#E12D39',
width: 280,
height: 50,
justifyContent: 'center',
alignSelf: 'center',
borderRadius: 10,
marginTop: 40,
marginBottom: 40,
},
text: {
alignSelf: 'center',
fontFamily: 'OpenSans-Semibold',
fontSize: 22,
color: 'white',
},
});
const SearchButton = ({ onPress, text }) => {
return (
<TouchableOpacity style={styles.view} onPress={onPress}>
<Text style={styles.text}>{text}</Text>
</TouchableOpacity>
);
};
export { SearchButton };
Expected behavior is for the Button to go down. Tried to remove every single flex property, but could not quite figure it out yet
seems like the height is not increasing when it wraps, could confirm it by making the container bg a different color
Looks like you hard coded the height to be 30, which is probably causing the bug