I am currently working on a React Native app with the following layout:
(full source code can be found at https://snack.expo.io/#ajaybhatta49/new-project, but I think the issue is in this file)
import React, {useState} from 'react'
import { View, StyleSheet } from 'react-native'
import CButton from './components/CButton'
import Display from './components/Display'
const buttonRows = [
["1", "2", "3", "+"],
["4", "5", "6", "-"],
["7", "8", "9", "x"],
[".", "0", "<", "/"],
["CLEAR", "ENTER"]
]
export default function App(props) {
const [input, setInput] = useState('')
const handlePress = str => {
if (str === "CLEAR") {
setInput('')
} else if (str === "ENTER") {
try {
setInput(eval(input))
} catch(err) {
setInput("SYNTAX ERROR")
}
} else if (str === "x") {
setInput(`${input}*`)
} else if (str === "<") {
setInput(input.slice(0, input.length - 1))
} else {
setInput(input + str)
}
}
return (
<View style={styles.main}>
<Display value={input}/>
{buttonRows.map(buttons => {
return (
<View style={styles.inline}>
{buttons.map(b => {
return (
<CButton title={b} handlePress={() => handlePress(b)}/>
)
})}
</View>
)
})}
</View>
)
}
const styles = StyleSheet.create({
main: {
flex: 1,
flexDirection: 'column',
alignContent: 'center',
justifyContent: 'space-evenly',
alignItems: 'center',
backgroundColor: '#222',
padding: 5
},
inline: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
height: 5
}
})
The app works fine, but I want to get rid of the extra space between the rows of buttons, and preferably align everything to the center vertically. I have tried changing the value of styles.main.justifyContent to space-evenly, space-around, flex-start, and center, none of which resolved the issue. I also tried a similar question's answer, which was to get rid of one of the flex: 1's, but the app crashes unless both of them are there. What should I do?
Whether justifyContent works vertically or horizontally depends on flex direction.So in styles.inline when you use flex while your direction is column, it makes the column take up all the vertical space it could. I removed height because it appearred to break the layout (idk why)
const styles = StyleSheet.create({
main: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#222',
padding: 5
},
inline: {
flexDirection: 'row',
}
})
Related
Coming from react-native, i am trying to build a component to display data with. What i am having trouble with, is combining the styles defined within the component itself, with the ones being passed as props from outside.
In react-native this is simply achieved by putting the 2 styleobjects inside an array, but how do i do this in react?
export interface MenuItemProps {
'containerStyle'?: React.CSSProperties,
}
export const MenuItem: React.FC<MenuItemProps> = (props) => {
const { title, selected, onClick, containerStyle } = props;
const mystyle = {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
marginTop: 10,
marginBottom: 10,
}
return (
<React.Fragment>
<div
style={[{mystyle, containerStyle}]}
onClick={() => onClick()}
You can combine the styles via rest operator to combine two objects with styles in one in prop style.
style={{...mystyle, ...containerStyle}}
what i ended up doing was add the incoming containerStyle to the end of myStyle, so that incoming styling overwrites existing styling;
const myStyle: React.CSSProperties = {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
marginTop: 10,
marginBottom: 10,
...containerStyle,
}
trying to create a day picker with a flat list. Basically i render multiple weeks that i can scroll through eg week1, week2, week3 etc. I want a single week to take up the width of the device, and when i am scrolling, I want it to "snap" to a week so I will always see the days "sun-sat" and nothing else. But I currently see like 1.75 weeks per screen width. I tried experimenting with justifyContent and flexGrow but it makes it too wide. I just need to scroll one item/ week at a time.
const Week = ({ item, selectedId, setSelectedId }) => (
<View style={styles.weekContainer}>
{item.days.map((day) => {
return (
<TouchableOpacity
onPress={() => setSelectedId(day.id)}
style={[
day.id == selectedId ? styles.unselectedDay : styles.selectedDay,
]}
key={day.id}
>
<Text
style={[
day.id == selectedId ? styles.unselectedDayText : styles.selectedDayText
]}
>
{day.name}
</Text>
</TouchableOpacity>
)
})}
</View>
);
export const DateHeader = () => {
const [selectedId, setSelectedId] = useState(today.toLocaleDateString());
const width = Dimensions.get('window').width
const renderItem = ({ item }) => {
return (
<Week
item={item}
selectedId={selectedId}
setSelectedId={setSelectedId}
/>
);
}
return (<>
<FlatList
data={weeks}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
horizontal={true}
inverted={true}
style={styles.flatList}
showsHorizontalScrollIndicator={false}
decelerationRate={0}
snapToInterval={width}
snapToAlignment={'center'}
/>
</>)
}
Styles:
const styles = StyleSheet.create({
flatList: {
flexGrow: 0,
},
weekContainer: {
flexGrow: 1,
flexDirection: 'row',
borderColor:'red',
borderWidth: '2px',
},
unselectedDay: {
fontSize: 16,
padding: 10,
height: 50,
backgroundColor: '#6e3b6e',
},
selectedDay: {
fontSize: 16,
padding: 10,
height: 50,
backgroundColor: '#f9c2ff',
},
unselectedDayText: {
textColor: 'white'
},
selectedDayText: {
textColor: 'black'
},
});
Thank you in advance!
The code to render a TabList:
import React, { Children, useEffect } from 'react';
import { LayoutChangeEvent, View } from 'react-native';
import {
ScrollView,
TouchableWithoutFeedback,
} from 'react-native-gesture-handler';
import Animated, {
Easing,
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated';
import { isValidChild } from '#utils';
import { useTabIndex } from '../tab-context';
import { useStyle } from './tab-list.styles';
import { TabListProps } from './tab-list.type';
const animConfig = {
duration: 200,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
};
const TabList: React.FC<TabListProps> = props => {
const styles = useStyle();
const { children, onChange } = props;
const selectedTabIndex = useTabIndex();
const animatedTabIndicatorPosition = useSharedValue(0);
// Save layout of the container
const [containerLayout, setContainerLayout] = React.useState({
x: 0,
y: 0,
width: 0,
height: 0,
});
const onContainerLayout = (event: LayoutChangeEvent) => {
const { x, y, width, height } = event.nativeEvent.layout;
setContainerLayout({ x, y, width, height });
};
// get children length
const childrenLength = Children.count(children);
const tabWidth =
childrenLength > 3
? containerLayout.width / 3
: containerLayout.width / childrenLength;
const renderChildren = () => {
// Render only children of component type TabList
return Children.map(children, child => {
// Check if child is a valid React element and has type TabList
if (isValidChild(child, 'Tab')) {
return (
<TouchableWithoutFeedback
containerStyle={{ width: tabWidth }}
onPress={() => onChange((child as JSX.Element)?.props.tabIndex)}
>
{child}
</TouchableWithoutFeedback>
);
}
// Throw error if child is not a TabList
throw new Error('TabList component can only have children of type Tab');
});
};
useEffect(() => {
animatedTabIndicatorPosition.value = selectedTabIndex * tabWidth;
}, [selectedTabIndex]);
const indicatorAnimatedStyle = useAnimatedStyle(() => {
return {
width: tabWidth,
transform: [
{
translateX: withTiming(
animatedTabIndicatorPosition.value,
animConfig,
),
},
],
};
}, []);
return (
<View onLayout={onContainerLayout} style={styles.container}>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
testID="TestID__component-TabList"
>
<Animated.View
style={[styles.indicatorContainer, indicatorAnimatedStyle]}
>
<View
style={[
styles.indicator,
{
width: tabWidth - 4,
},
]}
/>
</Animated.View>
{renderChildren()}
</ScrollView>
</View>
);
};
export default TabList;
The styles for the component elements:
import { createUseStyle } from '#theme';
// createUseStyle basically returns (fn) => useStyle(fn)
export const useStyle = createUseStyle(theme => ({
container: {
position: 'relative',
flexGrow: 1,
backgroundColor: theme.palette.accents.color8,
height: 32,
borderRadius: theme.shape.borderRadius(4.5),
},
indicatorContainer: {
position: 'absolute',
height: 32,
justifyContent: 'center',
alignItems: 'center',
},
indicator: {
height: 28,
backgroundColor: theme.palette.background.main,
borderRadius: theme.shape.borderRadius(4),
},
}));
I am using react-native-reanimated to animate the tab indicator. What I noticed is, on app reload, the initial tab indicator position keeps on changing as seen in the GIF I have attached. At times, it is positioned where it should be and at times, half the box is hidden behind the scrollview container. When I remove the alignItems: center from the Animated.View, things work as expected.
I am perplexed as to why the position keeps changing because of align-items?
The issue was that the child indicator component wasn't wrapped within the boundary of the indicator container. I resolved this by adding flexWrap: 'wrap' to the parent indicator container.
So, the new style looks like this:
import { createUseStyle } from '#theme';
// createUseStyle basically returns (fn) => useStyle(fn)
export const useStyle = createUseStyle(theme => ({
container: {
position: 'relative',
flexGrow: 1,
backgroundColor: theme.palette.accents.color8,
height: 32,
borderRadius: theme.shape.borderRadius(4.5),
},
indicatorContainer: {
position: 'absolute',
height: 32,
justifyContent: 'center',
alignItems: 'center',
flexWrap: 'wrap'
},
indicator: {
height: 28,
backgroundColor: theme.palette.background.main,
borderRadius: theme.shape.borderRadius(4),
},
}));
Edit: disregard the below, the behavior is not due to hot reloading! I'll leave this up in case anyone else has the same misconception
Hot reloading is not reliable with Reanimated - there are values on native threads that won't get refreshed. This has no impact on the final app.
To test whether it's really working, simply shake the device/sim and hit Reload after you make changes. This is enough to clear any sticky values. If your component still isn't doing what you want, you can then have the confidence to edit it and be sure it looks right.
<ThemeProvider theme={theme}>
I am using a theme provider, and I want to use two themes on the same DOM element.
style={theme.flexRowLeft}
I want to use two themes at the same time, but right now I can only use one element, and I have no idea how to do what I want to do.
const theme = createMuiTheme({
flexRow: {
display: "flex",
flexDirection: "row",
alignItems: "center",
},
justifyRight: {
justifyContent: "right",
},
});
How do I combine flexRow and justifyRight?
I am using Material-UI's themeprovider.
As suggested here
assuming you got theme through the useTheme hook inside your functional component:
const theme = useTheme()
you can try string interpolation:
<div className={`${theme.flexRow} ${theme.justifyRight}`}/>
so in total, for example:
const someComponent = props => {
const theme = useTheme();
return(<div className={`${theme.flexRow} ${theme.justifyRight}`}/>);
}
note that you should use className property, and not the style!
In one file you create files with themes like themes.js
where you put:
export const theme = createMuiTheme({
flexRow: {
display: "flex",
flexDirection: "row",
alignItems: "center",
},
justifyRight: {
justifyContent: "right",
},
});
And when you want to use it in functional component you write something like:
import clsx from "clsx";
import { makeStyles } from "#material-ui/core/styles";
const myStyles = makeStyles(theme => ({
AA: {
display: theme.flexRow.display,
flexDirection: theme.flexRow.flexDirection,
alignItems: theme.flexRow.alignItems
},
BB: {
justifyContent: theme.justifyRight.justifyContent
}
}), {name: "StyleNameVisibleInCss"});
function myFunctionalComponent() {
const classes = myStyles();
return (
<div className={clsx(classes.AA, classes.BB)}> Some text </div>
)
}
I'm trying to make custom style in react native text input. it's 6 textInput with little margin between them, like in the picture
that's my code, (temText is amount of textInput I want to create)
const { container, pinInputStyle,pinInputStyle2 } = styles;
const {styleUnder} = this.state;
var indet = this.temText.map((i) => {
return (
<TextInput
ref={ref => this.temText[i] = { myRef: ref, next: i + 1 }}
type={'TextInput'}
underlineColorAndroid={'transparent'}
autoCapitalize={'none'}
autoCorrect={false}
onChangeText={(value) => this._onChangeText(value)}
placeholder={this.props.placeholder}
keyboardType={Platform.OS === 'ios' ? 'number-pad' : 'numeric'}
autoFocus={i === 0}
maxLength={1}
key={i}
onFocus={()=>this._onFocus()}
onBlur = {()=>this._onBlur()}
placeholderTextColor ="#3e445f"
//onEndEditing = { this.verifyCode.bind(this) }
enablesReturnKeyAutomatically={false}
style={[pinInputStyle]}
/>
)
});
the styles
export const styles = StyleSheet.create({
container:{
flexDirection:'column',
flex:1,
},
textViewStyle:{
flexDirection:'row',
},
pinInputStyle:{
marginLeft:calcSize(10),
fontSize:calcSize(50),
width:calcSize(20),
alignSelf: 'center'
},
pinInputStyle2:{
fontSize:calcSize(150),
},
codeStyle:{
flex:1,
flexDirection:'row'
}
})
for now it's looks like this
but I need it more bigger the line\placeholder and the fontsize