How to "lock" the position of a component on react native screen? - react-native

I am trying to build the profile screen of a user on my social networking app. The way I want it is the top 1/3 of the screen is the user profile information, and the bottom 2/3 is a flatlist of all the user's posts. Currently, I have that all working logic wise. Here is a screenshot from the app:
As you can see, the user profile information is displaying up top, and the user's post history is correctly being displayed in a flat list below. However, the flat list is pushing the user profile information "up" on the screen.
Here is the code for the profile:
class Profile extends React.Component {
constructor(props) {
super(props)
this.state = {
user: this.props.user
}
}
goToSettings = () => {
this.props.navigation.navigate('Settings')
}
render() {
return (
<View style={styles.container}>
<View style={{ flexDirection: "row", padding: 20 }}>
<Text style = {styles.subheader}> {this.state.user.username} </Text> <------The username is being pushed up, where only the bottom half of it is visible
<TouchableOpacity
onPress={this.goToSettings}>
<Ionicons name="ios-settings" size={24} color="black" />
</TouchableOpacity>
</View>
<View style = {styles.lineStyle} />
<View style={{ flexDirection: "row" }}>
<ProfilePic/>
<ProfileStats/>
</View>
<View style = {styles.lineStyle} />
<ProfileBio />
<View style = {styles.lineStyle} />
<View style={{ flexDirection: "row", alignItems: 'center', justifyContent: 'center' }}>
<CurrentUserPostFeed navigation = {this.props.navigation} /> <------ This is the flat list. It is pushing up the views & other components that are above it.
</View>
</View>
)
}
}
I want the position of the user profile information to stay in the same postition, taking up the top 1/3 of the screen, while the user can scroll through the flatlist which only takes up the bottom 2/3 of the screen
However, I am not sure how to "lock" the profile information in place, where it is always visible in the top 1/3 of the profile screen, no matter how big/small the flatlist of posts becomes. Any advice?
EDIT: It seems that the margin/padding in the cells of the flatlist are causing my issue. Is there a way that I can prevent the flatlist container from pushing the elements above it "up" when I add marginTop or paddingTop?

You should use the flex property to set which
specifies how much of the remaining space in the flex container should be assigned to the item
More info about the flex property here.
** flex is a shorthand for flex-grow.
So, you can do something like this:
<View> // main container
<View style={{flex: 1}}> // header container
// Header Content
</View>
<View style={{flex: 2}}> // flatlist container
// Flatlist content
</View>
</View>
Basically what we are doing here is setting the header to use 1/3 of the available space of main-container and flatlist to use 2/3 of the available sapce of the main-container.

Related

React Navigation Stack doesn't cover whole screen on desktop

I'm using React Native Web to build a simple Reddit Clone.
I came across this issue while using a StackNavigator from React Navigation.
The stack 'screen' seems to get calculated by the dimensions of the monitor, instead of the content. So when a screen before the currently navigated screen has a larger height than the current screen, the content is still visible 'behind/below' the stack screen.
My homescreen:
<View style={{ flex: 1, backgroundColor: colors.background }}>
<Button style={{ margin: 5 }} action={() => props.navigation.navigate('CreatePost')}><DText>Create post</DText></Button>
<FlatList
data={getPosts}
renderItem={renderPost}
keyExtractor={item => item.id}
/>
</View>
My createpost screen:
<View style={{ flex: 1, backgroundColor: 'red' }}>
<View style={styles.element}>
<TextInput
placeholder={'Title'}
onChangeText={setTitle}
value={getTitle}
/>
<View style={styles.editor}>
<SlateEditor setBody={setBody} />
</View>
<Button action={() => onCreate(getTitle, getBody)}><DText>Post</DText></Button>
</View>
</View>
Here is an example:
I don't want to the user to be able to scroll on the CreatePost screen.
This is fixed by updating #react-navigation/stack to 5.12.6.
See https://github.com/react-navigation/react-navigation/commit/da35085f1e3440f26eea800c892c88aec64d072f

Determine touch side in react-native

I'm trying to make a touch system like Instagram in react-native. How can I determine if the user has touched on the right or left side of the screen? Is it possible to use swipe without extraneous libraries?
You can get the touch coordinates like this:
<View onTouchStart={(e) => {console.log('touchMove',e.nativeEvent)}} />
So you can define on a function it's limits.
Or, you can do it the "Bad way", That would be using 2 transparent touchable views (touchable-opacity, pressable, touchablehighlight) side by side, each one taking half of the screen.
like this (I didn't tested it):
<View style={{flexDirection: 'row', position: 'absolute', flex: 1}}>
<touchableOpacity style={{flex: 1}} onPress={() => console.log('left side clicked')}>
</touchableOpacity>
<touchableOpacity style={{flex: 1}} onPress={() => console.log('right side clicked')}>
</touchableOpacity>
</View>

Flexbox layout react native - first view occupies entire screen

I am trying to make a simple single page react native app.
Just to check if my flexbox layouts are working correctly,I decided to give background colors and check if they are aligned.
As seen in the picture the first view occupies the entire space.
Where I am I going wrong ?
My output as shown
Output
Here is my code:
render() {
return (
<View style={styles.container}>
<View style={styles.questionview}>
<Text>Enter a word to get meaning</Text>
<TextInput onChangeText={(text)=> this.setState({word:text})}/>
<Button title={"GET MEANING"} onPress={this.buttonPressed}/>
</View>
<View styles={styles.ansview}>
<Text styles={styles.anstext}>{this.state.answer}</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex:1
},
questionview:{
flex:6,
backgroundColor:'red',
padding:30
},
ansview:{
flex:3,
backgroundColor:'green'
}
});
Took me some time, but found your problem. It's a typo.
It should be just 'style' , not 'styles'.
It should have been as below :
<View style={styles.ansview}>
<Text style={styles.anstext}>{this.state.answer}</Text>
</View>
also styles.anstext is not present in your code.

React Native FlatList last item visibility issue

I am fetching products list and then displaying using a FlatList, my list contains 5 items and as you can see FlatList row height is variable because of varying description text. So the issue is my last item card is not completely visible maybe this is some kind of flat list issue or layout issue. Any help would be highly appreciated
renderProducts() {
if (this.props.loading === true) {
return (
<View style={Styles.spinnerStyle}>
<ActivityIndicator size='large' />
</View>
);
}
return (
<FlatList
data={this.props.myProducts}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<Card
title={item.title}
image={{
uri: item.image !== null ? item.image.src :'../resImage.jpg'
}}
>
<Text style={{ marginBottom: 10 }}>
{item.body_html}
</Text>
<Button
icon={{ name: 'code' }}
backgroundColor='#03A9F4'
fontFamily='Lato'
buttonStyle={{ borderRadius: 0, marginLeft: 0, marginRight: 0, marginBottom: 0 }}
title='VIEW NOW'
/>
</Card>
)}
/>
);
}
render() {
return (
<View>
<View style={Styles.viewStyle}>
<Text style {Styles.textStyle}>ProductsList</Text>
</View>
{
this.renderProducts()
}
</View>
);
}
Set bottom padding to the <FlatList> content container:
<FlatList
contentContainerStyle={{ paddingBottom: 20 }}
/>
Add {flex: 1} to the View tag housing the Flatlist component.
In my case,
const App = () => {
return (
<Provider store={createStore(reducers)}>
<View style={{ flex: 1 }}>
<Header headerText={'My App'} />
<ScreenTabs /> // this is my content with FlatList
</View>
</Provider>
);
};
export default App;
Just wrap it in a view with flex:1
<ParentView style={{flex:1}
<View style={{flex:1}}>
// Your flatlist
<View>
</ParentView>
Also, note that the each parent of this "View" in which Flatlist is wrapped must also be a View with Flex of 1. Otherwise, that your flatlist wont be visible.
use contentContainerStyle props of FlatList
<FlatList contentContainerStyle={{ paddingBottom: 20}} />
Latest update:
react-navigation has a SafeAreaView with an option to not show that bottom area.
import { SafeAreaView } from 'react-navigation';
<SafeAreaView forceInset={{ bottom: 'never' }} />
Old response below:
You can't see your list with flex: 1 because flex: 1 will grow the component to the parent. If the parent doesn't have flex: 1, it won't stretch to its parent or the screen. Keep in mind, however, that flex: 1 with a SafeAreaView will cause the bottom safe area to show. This will look bad if your SafeAreaView backgroundColor is a different color from your list's back ground.
My old workaround was to add an item to the bottom of the array of items, but I'm still exploring how to scroll past/under the bottom safe area margin with a FlatList (which is how I found this post to begin with).
Update: Using ListFooterComponent you can create even a plain white "footer" with height and/or a margin
For example (I wouldn't directly copy and paste this if I were you... there's surely a better way to detect bezel-less iPhones, especially in 2019 when we have more than one)
ListFooterComponent={<View style={{ height: 0, marginBottom: 90 }}></View>}
This is how I would do it, using the iPhoneX's height for now. But it's not future-proof since the conditional will need to be updated every time a new iPhone with no bezels comes out:
ListFooterComponent={<View style={{ height: 0, marginBottom: noBezels ? 90 : 0 }}></View>}
Or you could just always have some spacing at the bottom, like a loading gif, a message... whatever.
UPDATE 2:
I found out about react-native-device-info which has a hasNotch() method. I find that useful for styling for iPhones with no bezels by combining hasNotch() with Platform.OS === 'ios'
You can try this solution
For Vertical FlatList:
<FlatList
ListFooterComponent={<View />}
ListFooterComponentStyle={{height:200}}
/>
For Horizontal FlatList:
<FlatList
contentContainerStyle={{paddingRight:40}}
/>
For IOS issues you can apply some IOS specific props:
<FlatList
// ...
contentInset={{top: 0, bottom: 20, left: 0, right: 0}}
contentInsetAdjustmentBehavior="automatic"
// ...
/>
The solution with contentContainerStyle padding didn't seem the best overall for fixing the safe area IOS issues in my case.
Work very well for me
<FlatList
data={data}
contentContainerStyle={{ paddingBottom: 30 }}
style={{height: '95%'}}
renderItem={({ item, index }) => (
<ListItem item={item} onPress={() => handlePress(item, index)} />
)}
/>
Make use of the contentContainerStyle prop in the flatlist
<FlatList contentContainerStyle={{paddingBottom: 10}} />
For dynamic flatlist, you can assign height to the parent view. I resolved it with same.
<View style={{height:'80%'}}>
<Flatlist
extraData={data}
data={data}
renderItem={renderItem}
/>
</View>
I had the same issue and found the solution. To fix the issue just add style={{flex: 1}} for each View element who is a parent for FlatList.
See updated code below.
render() {
return (
<View style={{flex: 1}}> // Here you should add style as {flex: 1}
<View style={Styles.viewStyle}>
<Text style={Styles.textStyle}>ProductsList</Text>
</View>
{ this.renderProducts() }
</View>
);
}
This worked for me.
<View style={{flex: 1}}>
<FlatList
style={{flex: 1}}
data={data}
renderItem={({item}) => (
<ListItem item={item} onPress={() => handlePress(item)} />
)}
/>
</View>
#krish solution is great for the fixed-size list items, however as
#Neeraj Sewani said, it may not be suitable for dynamic size list items.
so you can fix the issue like this -in case direction is column -:
<View style={{height: '90%'}}>
<FlatList/>
</View>
Otherwise, -in case direction is row -:
<View style={{height: '90%', width:'90%'}}>
<FlatList/>
</View>
I was seeing this same problem in our Android + iOS React Native hybrid app. We embed the FlatList component within our native UIs inside a Fragment in Android and we were unable to scroll to the last item in the list, even though the scroll indicator would show that there was more to scroll, the ScrollView would simply not scroll further. I tried all the combinations of using a wrapping <View style={{flex:1}}> to wrap the FlatList as well as using contentContainerStyle={{flexGrow:1}} on the FlatList without success. Pursuing the clue further it turned out that the FlatList needs an absolute, predefined height on Android to allow scroll to the bottom - it works just fine on iOS but on Android using match_parent wasn't going to work. Since we need to support all types of devices, phone and tablet too, it wasn't possible to pre-define an absolute height either.
To fix this, I made a custom FrameLayout subclass to house the ReactRootView's fragment, which overrides onLayout() to ignore the child view measurements, forcing the views to have the exact dimensions of the FrameLayout, somewhat like so in Kotlin:
class StretchFrameLayout #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
for (child in children){
if (child.visibility == View.GONE) continue
child.updateLayoutParams {
this.width = measuredWidth
this.height = measuredHeight
}
if (needsRelayout){
handler.postDelayed({child.requestLayout()},1)
}
}
super.onLayout(changed, left, top, right, bottom)
}
}
This work very well in my case:
<FlatList
data={todos}
contentContainerStyle={{ height: '100%' }}
renderItem={({ item }) => <Todos items={item} pressed={pressed} />}
/>
I've solved it doing contentInset={{ bottom: data.length * itemStyle.height, }} with itemStyle.height being 50 worked fine.

Why my react native navigation bar's text not on the vertical center?

I use the Navigator.NavigationBar component to make a navbar, but the text not vertical centering.
Like this:
How can I solve this?
I just ran into this issue too. The solution:
<Navigator.NavigationBar
routeMapper={{
Title: (route, navigator, index, navState) =>
{
return (
<View style={{
justifyContent: 'center',
flex: 1
}}>
<Text style={{color: 'white'}}>Candidates</Text>
</View>
);
},
}}
/>
So basically make sure there's a parent wrap on your navbar elements filling the navbars height (the flex: 1 rule) and vertically center its contents with justifyContent: 'center'.
I've done the styles inline for this example, but of course it'd be more efficient to get those from a StyleSheet object.