How to set different heights on columns with flex-direction: 'row' - css

I'm trying to set container with two columns with different heights.
I have my container which has flex-direction: 'row' and flexWrap: 'wrap'.
Now I want to display there elements (only two can fit into container width) with different heights. I know that this can't be done with just flexbox, because rows has to be the same height when wrapping. So my question is: can this be done in React Native when Flexbox is default display type?
Code:
Container:
import React from 'react';
import {StyleSheet, View} from 'react-native';
import Reminder from './../components/reminders/Reminder';
const reminders = require('../../public/reminders.json');
const RemindersPage = () => {
return (
<View style={styles.reminders}>
{reminders.map((reminder) => {
return <Reminder key={reminder.id} reminder={reminder} />;
})}
</View>
);
};
const styles = StyleSheet.create({
reminders: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
},
});
export default RemindersPage;
Element:
import React from 'react';
import {StyleSheet, Text, View, Image} from 'react-native';
const Reminder = ({reminder}) => {
let source = require('./../../../public/forest.jpg');
return (
<View style={styles.reminder}>
<View style={styles.imageContainer}>
<Image style={styles.image} source={source} />
</View>
<View style={styles.reminders}>
{reminder.alerts.map((alert) => {
return (
<View style={styles.alert} key={alert.time}>
<Text style={styles.time}>{alert.time}</Text>
</View>
);
})}
</View>
</View>
);
};
const styles = StyleSheet.create({
reminder: {
minWidth: '48%',
height: 'auto',
backgroundColor: '#333',
color: 'white',
padding: 5,
marginVertical: 4,
borderStyle: 'solid',
borderWidth: 1,
borderColor: 'white',
alignSelf: 'flex-start',
},
imageContainer: {
alignItems: 'center',
borderStyle: 'solid',
borderBottomWidth: 1,
borderColor: 'white',
paddingBottom: 8,
},
image: {
width: '100%',
height: 150,
resizeMode: 'cover',
},
alerts: {
flexDirection: 'column',
marginTop: 5,
},
alert: {
flexDirection: 'row',
},
time: {
fontWeight: 'bold',
color: 'white',
textShadowColor: 'black',
textShadowOffset: {width: 0, height: 0},
textShadowRadius: 5,
},
});
export default Reminder;
My goal is that the element with red frame goes up as the arrow is pointing.
It should looks like this:
I don't want to set flex-direction to 'column' beacause I want this page to be scrollable.

Related

Stick two flex elements with dynamic growing in react-native

With react-native, I'm looking forward having a TextInput stuck with a MaterialIcons.Button within the same line.
I want the whole elements be centered horizontally but cannot achieve this with the following code:
import React from 'react';
import {
StyleSheet, TextInput, View,
} from 'react-native';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
const WordInput = () => {
return (
<View style={styles.container}>
<View style={styles.textInputContainer}>
<TextInput
textAlign="left"
/>
</View>
<View style={styles.arrowButtonContainer}>
<MaterialIcons.Button
name="arrow-forward-ios"
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
flex: 1,
},
textInputContainer: {
flex: 1,
alignItems: 'flex-end',
justifyContent: 'center',
},
arrowButtonContainer: {
flex: 1,
alignItems: 'flex-start',
justifyContent: 'center',
},
});
Here is the associated expo snack link.
The problem is that when I type text inside the TextInput, the Button doesn't move at all.
I would like it to dynamically shift to the right when the TextInput's width grow. The overall should be horizontally centered.
Does anyone know how I can proceed?
Thanks!
Unfortunately <TextInput> doesn't support any sort of "auto-growing" behaviour by default, but you could implement something yourself using a hidden <Text> layer that has the same font styling and sizing rules as your <TextInput>. If you then render your input value to that <Text> element, you can measure the layout of that element and apply the measured width to your <TextInput> component.
import { useState } from 'react';
import { StyleSheet, TextInput, View, Dimensions, Text } from 'react-native';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
export default function App() {
const [value, setValue] = useState();
const [containerWidth, setContainerWidth] = useState(
Dimensions.get('window').width
);
const [textWidth, setTextWidth] = useState(0);
const [buttonWidth, setButtonWidth] = useState(0);
const inputWidth = Math.min(textWidth, containerWidth - buttonWidth);
return (
<View style={styles.root}>
<View
style={styles.inner}
onLayout={(e) => setContainerWidth(e.nativeEvent.layout.width)}>
<Text
style={styles.hiddenText}
onLayout={(e) => setTextWidth(e.nativeEvent.layout.width)}
numberOfLines={1}
accessibilityElementsHidden
importantForAccessibility="no-hide-decendants">
{value}
</Text>
<TextInput
textAlign="left"
placeholder="enter text"
style={[styles.input, { width: inputWidth }]}
onChangeText={setValue}
/>
<View onLayout={(e) => setButtonWidth(e.nativeEvent.layout.width)}>
<MaterialIcons.Button name="arrow-forward-ios" />
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
root: {
flex: 1,
justifyContent: 'center',
},
inner: {
flexDirection: 'row',
justifyContent: 'center',
borderWidth: 1,
borderColor: 'red',
},
input: {
borderWidth: 1,
borderColor: 'green',
minWidth: 100,
},
hiddenText: {
position: 'absolute',
top: 0,
left: 0,
opacity: 0,
},
});
Here's an expo snack example.

How to create 3D button with gradient colour - React Native?

In React Native how would you build a button like the below?
I have managed to get this far:
However there is an issue with the border. As you can see there is a gap between the border radius and the linear gradient component. This seems to be an issue caused by using LinearGradient as it does not exist if the Linear Gradient component is replaced with a button component for example.
import React, { useState} from "react";
import {ImageBackground, StyleSheet, View, Text, TouchableOpacity } from "react-native";
import { LinearGradient } from 'expo-linear-gradient';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column"
},
marginContainer: {
flex: 1,
flexDirection: "column",
margin:20
},
buttonContainer:{
flex:3,
justifyContent: 'center',
alignContent: "center",
alignItems:"center"
},
modeButton:{
width:300,
height:100,
borderRadius:20,
borderRightWidth: 1,
borderLeftWidth:1,
borderBottomWidth: 14,
borderColor: '#024e51',
elevation:30,
shadowColor: 'rgba(0, 0, 0, 0.4)',
shadowOpacity: 0.8,
elevation: 30,
shadowRadius: 15 ,
shadowOffset : { width: 1, height: 13},
},
pressableArea:{
flexDirection:"row",
justifyContent: 'center',
alignItems: 'center',
width:"100%",
height:"100%"
}
});
function SelectModeScreen() {
return(
<View style={styles.container}>
<View style={styles.marginContainer}>
<View style={[styles.buttonContainer,{ backgroundColor: "darkorange" }]} >
<LinearGradient colors={['#5be9aa', '#09949d']} style={styles.modeButton}>
<TouchableOpacity style={styles.pressableArea} >
</TouchableOpacity>
</ LinearGradient>
</View>
</View>
</View>
)
}```
Here is the Working Example: Expo Snack
import React, { useState } from 'react';
import {
ImageBackground,
StyleSheet,
View,
Text,
TouchableOpacity,
} from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
},
buttonGrad: {
height: 50,
width: 200,
borderRadius: 10,
position: 'absolute',
bottom: 5,
},
buttonParent: {
height: 50,
width: 200,
borderRadius: 10,
backgroundColor: '#024e51',
},
});
function SelectModeScreen() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={() => console.log('btn pressed')}>
<View style={styles.buttonParent}>
<LinearGradient
colors={['#5be9aa', '#09949d']}
style={styles.buttonGrad}></LinearGradient>
</View>
</TouchableOpacity>
</View>
);
}
export default SelectModeScreen;
if you want to add shadow around the button add below style properties to above answer buttonParent style
...
buttonParent: {
...
shadowColor: 'black',
shadowOpacity: 1,
shadowOffset: {width: 2, height: 2},
shadowRadius: 10,
elevation: 8,
}
And by this properties you can make any type of elevated view

In React Native Flat List smash Views with 100% height and flex

I'm having trouble trying to display a list of Views using flex with 100% height, the issue comes when it renders the list from Flat list, I might do something wrong with flex.
This is what I want: A Flat list with same height when scrolls I will go to another component
And this is What I am having a smashed List of all the rendered views
The flatList is just this :
<FlatList
pagingEnabled={true}
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
And the container from the Views I am rendering looks like this (css)
container: {
alignItems: 'center',
// TODO: check how why the screen is forguetting the tab, thats why I put the height like that
width: '100%',
height: '100%',
justifyContent: 'space-around',
flex: 1,
},
Use onLayout to get the height of the parent container and use that to set the height of FlatList items:
Example Output:
Full source code:
//import { List, ListItem } from 'react-native-elements';
import React, { useState } from 'react';
import {
Text,
View,
FlatList,
StyleSheet,
SafeAreaView,
StatusBar,
} from 'react-native';
const attendants = [
{
courseName: 'comp',
lecturer: 'Akanbi T.B',
students: 'Tunde Ajagba',
date: '10/11/2020',
no: 1,
},
{
courseName: 'comp',
lecturer: 'Akanbi T.B',
students: 'Tunde Ajagba',
date: '09/11/2020',
no: 2,
},
{
courseName: 'comp',
lecturer: 'Akanbi T.B',
students: 'Tunde Ajagba',
date: '10/11/2020',
no: 3,
},
];
const App = () => {
const [data, setData] = useState(attendants);
const [layoutHeight, setHeight] = useState(100);
return (
<View style={styles.container}>
<View style={{ flex: 5 }}>
<FlatList
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
console.log(e.nativeEvent.layout.height);
}}
style={{ flexGrow: 1, backgroundColor: 'pink', height: layoutHeight }}
data={data}
keyExtractor={(item) => item.no}
renderItem={({ item }) => (
<View
style={{
height: layoutHeight
}}>
<ListItem
customStyle={{ backgroundColor: 'black' }}
title={`${item.lecturer} ${item.courseName}`}
subtitle={item.students}
/>
</View>
)}
/>
</View>
<View
style={{
flex: 1,
backgroundColor: 'lightgreen',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text style={[styles.subtitle, { fontSize: 20 }]}>Bottom Bar</Text>
</View>
</View>
);
};
const ListItem = ({ title, subtitle, customStyle }) => {
return (
<View style={styles.listContainer}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.subtitle}>{subtitle}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
marginTop: 30,
flex: 1,
},
listContainer: {
flex: 1,
backgroundColor: 'teal',
margin: 5,
paddingHorizontal: 5,
borderRadius: 4,
},
subtitle: { fontSize: 12, color: 'rgba(0,0,10,0.5)' },
title: {
fontSize: 14,
color: 'white',
fontWeight: 'bold',
textAlign: 'cetere',
},
});
export default App;
You can find the working app example here: Expo Snack
Flatlist is scrollview with many item inside and ScrollView content will calculator height of children render on it. So any item render on it need set height. height 100% will get height of parent and set for itself so you can not using height of scrollview content (infinity) and set for item inside.

How to fix marginHorizontal property in Flexbox not working?

I'm setting up some screens for an app using React-Native, and I want to fully understand how Flexbox works.
I'm trying to set the marginHorizontal property in style of textinput and button, but nothing happens.
If I set the specific value of width I do not have any problem.
Why marginHorizontal does not work properly?
The first image is what I get using marginHorizontal (the size of the button doesn't change and the margin from board is not 10);
The second image is what I get when I use width prop (the button size changes according the value of width).
I just wonder why with the marginHorizontal prop, nothing happens.
import React, { Component } from 'react'
import {Text, StyleSheet, View, TextInput, TouchableOpacity} from 'react-native'
import Swiper from 'react-native-swiper';
import {Button} from 'react-native-elements'
const formInputColor = 'rgba(255, 255, 255, 0.2)'
const formInputPlaceholderColor = 'rgba(255, 255, 255, 0.7)'
export default class RegisterFormComponent extends Component{
render(){
return(
<Swiper
style={styles.wrapper}
loop = {false}
>
<View style={styles.slide1}>
<View style={styles.topContainer1}>
<Text
style={styles.text}>What's you mail?
</Text>
<TextInput
placeholder = "Email"
placeholderTextColor = {formInputPlaceholderColor}
returnKeyType = "next"
style = {styles.formInput}
/>
<Text
style={styles.underText}
multiline={true}>blablablablablablablablablablablablablablablablablablablablablablablablablablablablablablabla
</Text>
<Button
style={styles.buttonStyle}
//textStyle = {styles.buttonTextStyle}
//loading={false}
>
</Button>
</View>
<View style={styles.bottomContainer1}>
</View>
</View>
<View style={styles.slide2}>
<View style={styles.topContainer1}>
<Text
style={styles.text}>Choose your password
</Text>
<TextInput
placeholder = "Password"
placeholderTextColor = {formInputPlaceholderColor}
secureTextEntry
returnKeyType = "go"
style = {styles.formInput}
/>
</View>
<View style={styles.bottomContainer1}>
</View>
</View>
<View style={styles.slide3}>
<Text style={styles.text}>And simple</Text>
</View>
</Swiper>
)
}
}
const styles = StyleSheet.create({
wrapper: {
flex: 1
},
slide1: {
flex: 1,
backgroundColor: '#3498db',
},
buttonStyle: {
backgroundColor: '#2980b9',
marginTop: 10,
height: 25,
marginHorizontal: 10
},
buttonTextStyle: {
fontSize: 10
},
topContainer1: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
logoStyle: {
width: 100,
height: 100,
},
bottomContainer1: {
flex: 1,
},
formInput:{
height: 40,
backgroundColor: formInputColor,
color: '#FFF',
marginHorizontal: 10,
paddingHorizontal: 10,
textAlignVertical: 'top'
},
slide2: {
flex: 1,
backgroundColor: '#97CAE5'
},
slide3: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#92BBD9'
},
text: {
color: '#000',
fontSize: 20,
padding: 10,
fontWeight: 'bold'
},
underText: {
color: 'black',
fontSize: 12,
marginTop: 8,
textAlign: 'center',
width: 300
}
})

Display: Inline Equivalent in React Native

It seems as though I am having problems with creating a display: inline styling equivalent with flexbox. So far I have achieved the following (where the red and blue lines are governed by the border function to help with styling):
With this code:
var React = require('react-native');
var {
View,
ScrollView,
Image,
StyleSheet,
Text,
TouchableHighlight,
} = React;
//additional libraries
var Parse = require('parse/react-native'); //parse for data storage
Icon = require('react-native-vector-icons/Ionicons'); //vector icons
//dimensions
var Dimensions = require('Dimensions');
var window = Dimensions.get('window');
//dynamic variable components
var ImageButton = require('../common/imageButton');
//var KeywordBox = require('./onboarding/keyword-box');
module.exports = React.createClass({
render: function() {
return (
<View style={[styles.container]}>
<Image
style={styles.bg}
source={require('./img/login_bg1_3x.png')}>
<View style={[styles.header, this.border('red')]}>
<View style={[styles.headerWrapper]} >
<Image
resizeMode={'contain'}
style={[styles.onboardMsg]}
source={require('./img/onboard_msg.png')} >
</Image>
</View>
</View>
<View style={[styles.footer, this.border('blue')]}>
<ScrollView
horizontal={false}
style={styles.footerWrapperNC}
contentContainerStyle={[styles.footerWrapper]}>
{this.renderKeywordBoxes()}
</ScrollView>
</View>
</Image>
</View>
);
},
renderKeywordBoxes: function() {
//renders array of keywords in keyword.js
//and maps them onto custom component keywordbox to show in the onboarding
//component
var Keywords = ['LGBQT', '#BlackLivesMatter', 'Arts', 'Hip-Hop', 'History',
'Politics', 'Comedy', 'Fashion', 'Entrepreneurship', 'Technology', 'Business',
'International', 'Health', 'Trending', 'Music', 'Sports', 'Entertianment'];
return Keywords.map(function(keyword, i) {
return <TouchableHighlight
style={styles.keywordBox}
key={i}
underlayColor={'rgb(176,224,230, 0.6)'} >
<Text style={styles.keywordText} >{keyword}</Text>
</TouchableHighlight>
});
},
//function that helps with laying out flexbox itmes
//takes a color argument to construct border, this is an additional
//style because we dont want to mess up our real styling
border: function(color) {
return {
borderColor: color,
borderWidth: 4,
}
},
});
styles = StyleSheet.create({
header: {
flex: 2,
},
headerWrapper: {
flex: 1,
flexDirection: 'column',
alignItems: 'center',
justifyContent:'space-around',
marginTop: window.height/35,
},
onboardMsg: {
width: (window.width/1.3),
height: (452/1287)*((window.width/1.3)),
},
footer: {
flex: 7,
marginTop: window.height/35,
},
//container style wrapper for scrollview
footerWrapper: {
flexWrap: 'wrap',
alignItems: 'flex-start',
},
//non-container style wrapper for scrollview
footerWrapperNC: {
flexDirection:'row',
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
bg: {
flex: 1,
width: window.width,
height: window.height,
},
actionButtonIcon: {
fontSize: 20,
height: 22,
color: 'white',
},
keywordText: {
fontFamily: 'Bebas Neue',
fontSize: 18,
padding: 6,
fontWeight: 'bold',
color: 'white',
letterSpacing: 1.5,
textAlign: 'center'
},
keywordBox: {
backgroundColor: 'transparent',
margin: 3,
borderColor: 'rgb(176,224,230, 0.6)',
borderWidth: 1,
},
});
But I would like to achieve this:
any ideas?
EDIT** ANSWER:
Needed to change the styling to the following:
//container style wrapper for scrollview
footerWrapper: {
flexWrap: 'wrap',
alignItems: 'flex-start',
flexDirection:'row',
},
//non-container style wrapper for scrollview
footerWrapperNC: {
flexDirection:'column',
},
So use of flexDirection in column and row for scrollView works children stay inline
Needed to change the styling to the following:
//container style wrapper for scrollview
footerWrapper: {
flexWrap: 'wrap',
alignItems: 'flex-start',
flexDirection:'row',
},
//non-container style wrapper for scrollview
footerWrapperNC: {
flexDirection:'column',
},
This works for me:
import {View, Text} from 'react-native';
<View style={styles.container}>
<Text>Hello</Text>
</View>
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignSelf: 'flex-start'
}
});

Resources