Stick two flex elements with dynamic growing in react-native - css

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.

Related

flex-direction row not working in React Native

I'm trying to create an Andy Warhol style image for an assignment.
The row container isn't rendering the images in a row, the goal being to stack the 2 row containers in to a square formation.
Here's my code so far:
import React, { Component } from 'react';
import { AppRegistry, Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<View style = {styles.rowContainer}>
<View style = {styles('blue').img}>
<View style = {styles('red').subImg}>
</View>
</View>
<View style = {styles('black').img}>
<View style = {styles('green').subImg}>
</View>
</View>
</View>
<View style = {styles.rowContainer}>
<View style = {styles('purple').img}>
<View style = {styles('yellow').subImg}>
</View>
</View>
<View style = {styles('orange').img}>
<View style = {styles('#11E1E4').subImg}>
</View>
</View>
</View>
</View>
);
}
}
const styles = (inputColor) => StyleSheet.create({
container: {
flex : 1,
flexDirection: "row",
},
rowContainer:{
height: 100,
width: 200,
flexDirection: "row",
},
img: {
height : 100,
width: 100,
alignItems: "center",
justifyContent: "center",
backgroundColor : inputColor,
},
subImg: {
height: 50,
width: 50,
backgroundColor : inputColor,
},
});
I've fiddled around with the nesting and the size of the row containers. The example code given to me by my teacher works as expected. I have no clue what is going wrong. Really new to coding btw so please dumb any answer down
Example Code:
import React, { Component } from 'react';
import { AppRegistry, Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.topBox}>
</View>
<View style={styles.middleBox}>
</View>
<View style={styles.bottomBox}>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
},
topBox: {
width: 75,
height: 75,
backgroundColor: 'lightblue',
},
middleBox: {
width: 75,
height: 75,
backgroundColor: 'mediumblue',
},
bottomBox: {
width: 75,
height: 75,
backgroundColor: 'darkblue',
},
});
If you use styles as a function then you need to call it when applying styles.
Add () after styles.
<View style={styles().container}>
<View style = {styles().rowContainer}>
Using typescript can help to avoid such errors.
You are not using flexbox. This is done by setting display: flex on the container. Therefore, set display: "flex" on the container.
const styles = StyleSheet.create({
container: {
display: 'flex',
flex: 1,
flexDirection: 'column',
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
},
...
Side note: When setting display: flex, the flex-direction (flexDirection) is row by default.

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

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.

Styling props not working, especially padding

I am still new to react-native and I have some troubles regarding the styling props of react-native.
I created a screen in my app and all my relevant code is this:
import React, {useState, useEffect} from 'react';
import {View, Text, StyleSheet, ActivityIndicator, Alert, ImageBackground} from 'react-native';
import { TextInput } from 'react-native-gesture-handler';
import {useDispatch, useSelector} from 'react-redux';
const NewLetterScreen = props =>{
return(
<View style={{flex:1}}>
<ImageBackground source={require('../components/pictures/welcome.png')} style={{width:"100%", height:"100%", alignItems: 'center', justifyContent: 'center'}}>
<ImageBackground source={require('../components/pictures/letters/paper1.jpg')} style={styles.paper}>
</ImageBackground>
</ImageBackground>
</View>
)
};
const styles = StyleSheet.create({
paper:{
width: "90%",
height: "90%"
}
});
export default NewLetterScreen;
The problem I face now is, that the "paper" I want to display is not centered, even tho I stricly declare this in the wrapping ImageBackground with justifyContent and alignItems. It is displaced to far up and to far on the left side and looks like this:
Also, if I try to use padding to get this stuff centered somehow, it doesnt work. I want to do padding from left and right, so horizontaly, but if I apply "paddingHorizontal: 50" to my styles.paper, the result is the following:
As you see, the "paddingHorizontal:50" works like a paddingLeft and that is definitely not what I intend.
I would be happy about any help regarding my styling props. Thanks in advance!
Why not add a margin of 10%? I always have problems with paddings. Try to avoid them.
Edit: Maybe it would help to just add a margin, instead of changing the width to 90%?
import React from "react";
import { ImageBackground, StyleSheet, Text, View } from "react-native";
const image = { uri: "https://reactjs.org/logo-og.png" };
const App = () => (
<View style={styles.container}>
<ImageBackground source={image} style={styles.image}>
<Text style={styles.text}>Inside</Text>
</ImageBackground>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
justifyContent: "center"
},
image: {
flex: 1,
resizeMode: "cover",
justifyContent: "center",
margin: "10%"
},
text: {
color: "white",
fontSize: 42,
fontWeight: "bold",
textAlign: "center",
backgroundColor: "#000000a0"
}
});
export default App;
You can copy&paste this example on https://reactnative.dev/docs/imagebackground
You just need to add a wrapper for the inside image. You don't need margins or paddings.
const image1 = { uri: "https://images.unsplash.com/photo-1597768130867-4c06bf86a2f3?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1950&q=80" };
const image2 = { uri: "https://images.unsplash.com/photo-1611085857146-7b13f8b0a74f?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=641&q=80" };
const App = () => (
<View style={{ flex: 1 }}>
<ImageBackground source={image1} style={styles.background}>
<View style={styles.wrapper}>
<ImageBackground source={image2}
resizeMode="cover"
style={styles.paper}>
</ImageBackground>
</View>
</ImageBackground>
</View>
);
const styles = StyleSheet.create({
background: {
width: "100%",
height: "100%",
alignItems: 'center',
justifyContent: 'center'
},
wrapper: {
width: "90%",
height: "90%",
alignItems: 'center',
justifyContent: 'center',
},
paper: {
width: "100%",
height: "100%"
}
});

CSS breaking when using padding

I have a JS class
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { Avatar, Accessory } from 'react-native-elements';
import PropTypes from 'prop-types';
import MenuButton from './MenuButton';
import AppStyles from '../AppStyles';
import Api from '../Api';
export default class DrawerContainer extends React.Component {
render() {
const { navigation, onLogout } = this.props;
return (
<View style={styles.content}>
<Avatar size={100}
rounded
source={{
uri:
'https://s3.amazonaws.com/uifaces/faces/twitter/ladylexy/128.jpg',
}}
/>
<View style={styles.container}>
<MenuButton
title="Home"
source={AppStyles.iconSet.home}
onPress={() => {
navigation.navigate('Home');
}}
/>
<MenuButton
title="Logout"
source={AppStyles.iconSet.logout}
onPress={() => {
onLogout();
Api.logout();
}}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
content: {
flex: 1,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
paddingTop: 75
},
container: {
flex: 1,
alignItems: 'center',
paddingHorizontal: 20,
alignItems: 'flex-end'
},
});
DrawerContainer.propTypes = {
navigation: PropTypes.object,
onLogout: PropTypes.func,
};
Here is the output generated from the same.
What I want to achieve is to have a gap between the image and the icons displayed. I tried using padding but the text overflows with the same. Can anyone suggest as to what wrong am I doing here?
container: {
flex: 1,
alignItems: 'center',
paddingHorizontal: 20,
alignItems: 'flex-end'
margin-top: 10%;
},
Try to reate a margin-top for .container

React Navigation - padding bottom on header not working

In my React-Native app I have an icon and SearchBar in my header (from react navigation).
The following code:
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerTitle:
<View style={{ flex: 1, flexDirection: "row", paddingHorizontal: 15, alignItems: "center" }}>
<StatusBar default style={{ flex: 1, height: getStatusBarHeight() }} />
<Icon name="chevron-left" size={28} />
<SearchBar round platform={"default"} placeholder="Search" containerStyle={{
flex: 1, backgroundColor: "transparent"
}} />
</View>,
headerStyle: {
backgroundColor: '#e54b4d',
}
};
}
outputs this:
So far so good. However, I want to have padding below the SearchBar. In other words, I want to have the distance from the top of the screen to the SearchBar as a padding below the SearchBar. (I can obtain the distance value using getStatusBarHeight() from rn-status-bar-height)
However, if I put paddingBottom: getStatusBarHeight() to the headerStyle, I get this result:
Basically, now I have the padding that I wanted, however, the StatusBar overlaps with the SearchBar.
How can I put paddingBottom without making the StatusBar and SearchBar overlap?
To change the padding of the header in your case you'll need to change headerTitleContainerStyle and not headerTitle.
For example :
headerTitleContainerStyle: { paddingVertical: 10 }
You can still check the doc.
For ios you will need to set backgroundColor.Below code is fit for android ios both.Hope it helps you.
import React, { Component } from 'react';
import { getStatusBarHeight } from 'react-native-status-bar-height';
import {
Modal,
Button,
View,
Text,
StyleSheet,
StatusBar,
Image,
Platform,
} from 'react-native';
import { SearchBar, Icon } from 'react-native-elements';
export default class AssetExample extends React.Component {
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerTitle: (
<View
style={{
flex: 1,
backgroundColor: Platform.OS === 'ios' ? '#e54b4d' : '',
alignItems: 'center',
flexDirection: 'row',
paddingHorizontal: 10,
height: StatusBar.currentHeight,
}}>
<Icon name="chevron-left" size={28} />
<SearchBar
round
platform={'default'}
placeholder="Search"
containerStyle={{
flex: 1,
backgroundColor: 'transparent',
}}
/>
</View>
),
headerStyle: {
backgroundColor: '#e54b4d',
},
};
};
render() {
return (
<View style={styles.container}>
<Text>Screen</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
});
when i checked your code on my device its properly viewing with padding.

Resources