How to use marginTop when view wrap to next row? - react-native

I have an array data , i map the array return <Button />, but i find that when the view wrap <Button /> to next row automatically, there is no spacing each row.
How do i know what time is the view wrap to next row ? So that i can control margingTop between each row.
I try to add position: 'relative' in my itemBody style, but there is no any different.
Any help would be appreciated. Thanks in advance.
Here is my view code:
<Body style={styles.timeBody}>
{releasedTime.map((value, index) => {
const userTime = GetUserTime.getUserHour();
const theTime = GetUserTime.getAsiaTime(value, 'YYYY/MM/DD HH:mm:ss');
const hour = theTime.getHours();
const minute = (theTime.getMinutes() < 10 ? '0' : '') + theTime.getMinutes();
if (userTime < hour) {
return (
<GrayButton key={index}>{`${hour}:${minute}`}</GrayButton>
);
}
return (
<Button key={index}>{`${hour}:${minute}`}</Button>
);
})}
</Body>
Here is my style for wrap:
const styles = {
timeBody: {
flex: 1,
flexDirection: 'row',
paddingTop: 5,
flexWrap: 'wrap',
width: '100%'
}
};

add marginTop on Your Button and customButtons
<Button style={{marginTop:5}} key={index}>{`${hour}:${minute}`}</Button>
Like this also add to customButton like <GrayButton/>

Related

Add data to begining of FlatList without changing position

I'm trying to implement an "onBeginReached" like props in flatlist. I would like to append some data at the begining of my data array in a transparent way to user.
So using this flatList :
const App = () => {
const flatListRef = useRef(null);
const [data, setData] = useState(generateData(20));
const renderItem = ({ item }) => {
console.log(item);
return (
<View style={styles.itemContainer}>
<Text style={styles.itemText}>{item}</Text>
</View>
);
};
const handleMomentumScroll = (event) => {
console.log("Momentum end")
const xOffset = event.nativeEvent.contentOffset.x;
const index = Math.round(xOffset / 30);
if (index < 1) {
setData([-10 ,-9, -8, -7, -6,-5, -3, -2, -1, ...data]);
}
};
return (
<FlatList
style={{ width: 200, alignSelf: 'center', marginTop: 150 }}
initialScrollIndex={10}
horizontal
data={data}
snapToAlignment={'start'}
decelerationRate={'fast'}
snapToInterval={30}
getItemLayout={(data, index) => ({
length: 30,
offset: 30 * index,
index,
})}
keyExtractor={(item, index) => index.toString()}
renderItem={renderItem}
onMomentumScrollEnd={handleMomentumScroll}
/>
);
};
const styles = StyleSheet.create({
itemContainer: {
alignItems: 'center',
justifyContent: 'center',
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: 'blue',
},
itemText: {
color: 'white',
},
});
(https://snack.expo.io/GUblotbZc)
If I scroll to the index 0, it'll unshift my new data to my data array. But, it'll scroll automatically to the first index of the new data array. I would like to keep the current position when unshifting new data to the array.
There is a way to impletement that behaviour ?
here is demo: https://snack.expo.io/#nomi9995/flatlisttest
use maintainVisibleContentPosition props for preventing auto scroll in IOS but unfortunately, it's not working on android but good news is pull request has come for android and need to merge with react native.
<FlatList
ref={(ref) => { this.chatFlatList = ref; }}
style={styles.flatList}
data={this.state.items}
renderItem={this._renderItem}
maintainVisibleContentPosition={{
minIndexForVisible: 0,
}}
/>
The way I did it is by inverting FlatList using the inverted prop, and also reversing my list. In this way, the top of FlatList will be at the bottom with last item in my array is visible there.
When user scrolls to the top onEndReached is triggered, and I add new items to the beginning of my array and they will be added to the top of FlatList with out changing the current visible item at the top.
You can use to Flatlist prop named "ListHeaderComponent" to add any component at the beginning of your Flatlist. https://reactnative.dev/docs/flatlist#listheadercomponent

How to add space between components that comes from a mapping function?

I am trying to add some buttons with a title that is inside every position of an array (for exaMple the first button have its title in the content of the array in the position 0), that is the reason i am using a map function and it works but... i can not add space between each button
it looks like this:
THIS IS WHERE THE MAPPING FUNCTION IS CALLED:
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
<View style={{
flexDirection: 'row',
justifyContent: 'space-between'}}>
{mapping()}
</View>
</ScrollView>
THIS IS THE FUNCTION:
function mapping() {
const horas = ["6:30-7:30", "7:30-8:30", "7:30-8:30"...]
const mappeo = horas.map((i) => {
return (
<Button
title={i}
type="outline"
style={{ padding:10}}/>
)
}
)
return (mappeo)
}
So how can i separate the buttons?
How about using margin. Padding are used for content inside a container and margins are used for applying margins or spaces outside the container.
Try this
function mapping() {
const horas = ["6:30-7:30", "7:30-8:30", "7:30-8:30"...]
const mappeo = horas.map((i) => {
return (
<Button
title={i}
type="outline"
style={{ padding:10, marginHorizontal:10}}/>
)
}
)
return (mappeo)
}
add 'marginHorizontal' to your button like below:
function mapping() {
const horas = ["6:30-7:30", "7:30-8:30", "7:30-8:30"...]
const mappeo = horas.map((i) => {
return (
<Button
title={i}
type="outline"
style={{ padding:10, marginHorizontal: 5 }}/>
)
}
)
return (mappeo)
}

json value '2' of type nsnumber cannot be converted to nsstring

I am working on a imageSlider using Carousel imported from library: "react-native-banner-carousel"
I am trying to fetch the images which are stored locally inside a folder components. I am creating an array if images and then trying to render them through Carousel.
I am certainly getting an error like: "json value '2' of type nsnumber cannot be converted to nsstring"
ImageSlider code:
const BannerWidth = Dimensions.get('window').width;
const BannerHeight = 260;
const images = [
require("./abc.jpg"),
require("./xyz.jpg")
];
export default class ImageSlider extends React.Component {
renderPage(image, index) {
return (
<View key={index}>
<Image style={{ width: BannerWidth, height: BannerHeight }} source={{uri:image}} />
</View>
);
}
render() {
return (
<View style={styles.container}>
<Carousel
autoplay
autoplayTimeout={5000}
loop
index={0}
pageSize={BannerWidth}
>
{images.map((image, index) => this.renderPage(image, index))}
</Carousel>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
justifyContent: 'center'
},
});
As a result I should be able to render all the images displayed inside the images array. But I am getting this error.
Where am I getting wrong here?
Your issue is here:
const images = [
require("./abc.jpg"),
require("./xyz.jpg")
];
The require does not return the URL of the image but the internal index of the media. Which in your case I assume is 2, and the interface for the image is expecting a string.
Try this:
<Image style={{ width: BannerWidth, height: BannerHeight }} source={image} />
For anyone that is searching this error for whom the top answer didn't work: I received this error in React(Native) and wrote marginTop: '5'. With Parenthesis. It should be marginTop: 5
Wrong:
marginTop: '5'
Correct:
marginTop: 5

React Native TabNavigator change TabStyle to follow according to the text

I am using expo v27.0, react native 0.55 and I as you can see in the picture that the tab have somewhat a fixed width like a default width from the tab navigation, and the text wrap into three lines, I want the text to be in 1 line and nowrap, and i have tried styling (flexWrap:
'nowrap', flex: 1) in TabStyle, LabelStyle in TabBarOptions, but still can't get the tab to have the width according to the text inside the tab.
I populate the text for the tabs dynamically from json using fetch, therefore all tabs will have different width according to the text. How to I make the tab to follow the width of the text ?
All answers are greatly welcomed.
Thank you in advance.
Solved, turns out just need to set the width to auto as follows:
tabBarOptions: {
tabStyle: {
width: 'auto'
}
}
You can use render label in render header and in that you can return your Text component and Text is having numberOfLines props that will be 1 and it will add ... at end of the text after one line.
Check example snippet:
_renderLabel = props => {
let preparedProps = {
style: {
fontFamily: fonts.Regular,
marginVertical: 8
},
fontType: props.focused ? "Medium" : "Light"
};
return (
<Text
{...preparedProps}
numberOfLines={1}
ref={ref => {
ref && this.props.addAppTourTarget(ref, props.route.key);
}}
>
{props.route.type === "free" && this.state.is_premium_member
? this.labels.premium
: props.route.title}
</Text>
);
};
_renderHeader = props => (
<TabBar
{...props}
bounces={true}
style={{
backgroundColor: colors.cardBlue
}}
indicatorStyle={{
backgroundColor: colors.radicalRed,
height: 1,
borderRightWidth: initialLayout.width * 0.1,
borderLeftWidth: initialLayout.width * 0.1,
borderColor: colors.cardBlue
}}
tabStyle={{
padding: 0,
borderTopColor: "transparent",
borderWidth: 0
}}
renderLabel={this._renderLabel}
/>
);
_handleIndexChange = index => this.setState({ index });
_renderScene = ({ route, focused }) => {
switch (route.key) {
case "a":
return <One {...this.props} route={route} focused={focused} />;
case "b":
return (
<Two {...this.props} isSeries={true} focused={focused} />
);
case "c":
return <Three {...this.props} route={route} focused={focused} />;
default:
return null;
}
};

React Native FlatList with columns, Last item width

I'm using a FlatList to show a list of items in two columns
<FlatList style={{margin:5}}
data={this.state.items}
numColumns={2}
keyExtractor={(item, index) => item.id }
renderItem={(item) => <Card image={item.item.gallery_image_url} text={item.item.name}/> }
/>
The card component is just a view with some styles:
<View style={{ flex: 1, margin: 5, backgroundColor: '#ddd', height: 130}} ></View>
It is working fine, but if the number of items is odd, the last row only contains one item and that item stretches to the full width of the screen.
How can I set the item to the same width of the others?
for your case use flex: 1/2
therefore: Your item should have flex of 1/(number of columns) if you have 3 columns your item should have flex:1/3
Theres a few things you can try here.
A) Setting a pre-defined width for the card ( Maybe equal to the height you've set? ). Then you can use alignItems in order to have the card positioned in the middle or on the left - Unsure as to which you wanted here.
B) If there are an even number of cards, you could add an empty View at the end in order to fill this space. I find this method pretty clunky but useful when trying to leave space for future elements.
C) Simply use alignItems: 'space-between, i like to use this to center items, but you would have to define the width, or use something like flex:0.5
I suggest researching more into flexbox to help you with this, as it is hard to tell the context of this situation. I'm assuming the above methods will help, but if not, here are some links for you to look at -
A Complete Guide to Flexbox (CSS Tricks)
Layout with Flexbox (React Native)
Video Tutorial (Youtube) Link Broken
Hope this helps. If you need any further clarification - just ask
This is the cleanest way to style a FlatList with columns and spaced evenly:
<FlatList style={{margin:5}}
numColumns={2} // set number of columns
columnWrapperStyle={style.row} // space them out evenly
data={this.state.items}
keyExtractor={(item, index) => item.id }
renderItem={(item) => <Card image={item.item.gallery_image_url} text={item.item.name}/> }
/>
const style = StyleSheet.create({
row: {
flex: 1,
justifyContent: "space-around"
}
});
You can try to get the current width of the device via Dimensions, do some math based on the number of columns you want to render, minus off the margins and set that as the minWidth and maxWidth.
For example:
const {height, width} = Dimensions.get('window');
const itemWidth = (width - 15) / 2;
<View style={{ flex: 1, margin: 5, backgroundColor: '#ddd', minWidth: {this.itemWidth}, maxWidth: {this.itemWidth}, height: 130}} ></View>
The reason for it is your Card have style flex: 1, so it will try to expand all the space remain.
You can fix it by add maxWidth: '50%' to your Card style
<View style={{ flex: 1, margin: 5, backgroundColor: '#ddd', height: 130, maxWidth: '50%'}} ></View>
#Emilius Mfuruki suggestion is good, but if you have text with varying length it doesn't work perfectly. Then use this width inside your item view:
const {height, width} = Dimensions.get('window');
const itemWidth = (width - (MarginFromTheSide * 2 + MarginInBetween * (n-1))) / n;
In FlatList use:
columnWrapperStyle={{
flex: 1,
justifyContent: 'space-evenly',
}}
Works perfectly.
The simplest solution is do the math.
Imagine we have 2 View for each Row and we want to give 10 margin to every side it will look something like that:
As you see in the image above each View have 2 margins in horizontal. (inside of red rectangle)
So we have to subtract the product of margin, number of column and 2 from the width.
import { Dimensions } from 'react-native';
const {width} = Dimensions.get("window")
const column = 2
const margin = 10
const SIZE = (width - (margin * column * 2)) / column
<View style={{ margin: 10, width: SIZE }} ></View>
I tried some of the solutions above but I still had some problems with the margins on the last item (2 columns list).
My solution was simply wrapping the item into a parent container, leaving the original container with flex: 1 and the parent container of the item with flex: 0.5 so it would take the margin correctly.
itemContainer: {
flex: 0.5,
},
itemSubContainer: {
flex: 1,
marginHorizontal: margin,
},
A simple way with flex
<FlatList style={{margin:5}}
data={this.state.items}
numColumns={2}
keyExtractor={(item, index) => item.id }
renderItem={({item, index}) => {
const lastItem = index === this.state.items.length - 1;
return (
<View style={{flex: lastItem ? 1 / 2 : 1 }}>
<Card image={item.gallery_image_url} text={item.name}/>
</View>
)}}
/>
You can use ListFooterComponent={this.renderFooter}
None of the above answers have worked perfectly for me so I post my own answer:
works with padding and margins
the last element will always have the correct size
<FlatList
data={data}
numColumns={2}
renderItem={({item, index}) => {
const lastItem = index === data.length - 1;
return (
<View style={{flex: 1, padding: 8, maxWidth: lastItem ? '50%' : '100%' }}>
...
</View>
)}}
/>
Note: change maxWidth according to number of columns
Result:
just use flex:0.5 and width:'50%'
Create an array with odd number of images in it, like:
const images = [
require('./../...jpg'),
require('./../...jpg'),
require('./../...jpg'),
require('./../...jpg'),
require('./../...jpg'),
];
And then, use the code given below,
const App = () => {
const _renderItem = ({ item, index }) => (
<Image
source={item}
style={{
width: '50%',
height: 200,
}}
resizeMode="cover"
/>
);
return (
<View style={{flex: 1, marginHorizontal: 10,}}>
<FlatList
columnWrapperStyle={{ justifyContent: 'space-between' }}
keyExtractor={(_, index)=> index.toString()}
data={images}
numColumns={2}
renderItem={_renderItem}
/>
</View>
)
};
export default App;
Working Example