Flex layout with ScrollView - android

I've been making Layouts for Screens in React Native with Flexbox for a while now and hadn't run into any trouble until today when I had to make this very simple layout:
A header section (not a NavBar).
A content section → A ScrollView with a FlatList inside of it.
A footer section.
I want the content section to be 3 times bigger than the Header and the Footer, so naturally I set the flex to 1 (header), 3 (content), 1 (footer).
No matter what, the content remains as if it has flex: 1.
The only way I could control the layout the way I wanted was to leave content's flex: 1 and set both footer and header to flex: 0.33.
I suspect it might have something to do with ScrollView's contentContainerStyle prop which I set to flexGrow: 1, flexShrink: 1.
Here's a minimal example which reproduces this.

Update 2:
I've been pointed out that I shouldn't be using a ListView wrapped inside a ScrollView since the following can happen:
It might lead to some weird behaviors like onEndReached firing continuously.
Wrapping the ListView inside the ScrollView makes the parent scroll action dominate the child scroll action which leads only ScrollView to scroll.
Okay, I'm not sure why this works, but found the solution after hours of trying:
After the step mentioned in Update 1, I added the following style flexGrow: 0 to the FlatList inside the ScrollView and the content became centered and decreasing the content's size until it becomes scrollable works perfectly as well.
Basically this is the resulting code:
render() {
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
<Text>HEADER</Text>
</View>
<View style={{ flex: 6 }}> // Change this to affect ScrollView size
<ScrollView contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }}>
<FlatList
data={listItems}
renderItem={Item}
keyExtractor={(index, item) => item}
style={{ flexGrow: 0 }} // This was the solution to centering
/>
</ScrollView>
</View>
<View style={{ flex: 1 }}>
<Text>FOOTER</Text>
</View>
</View>
);
}
Here's the final Working Solution if you'd like to try it.
Update 1:
Managed to control ScrollView size with Flex normally (that is using integer flex values as intended in the first place) by wrapping ScrollView inside a View with flex: 1.
The downside is that if I set the Scrollable section (content) to a flex value high enough so It's not scrollable anymore, content doesn't display centered. Not even if justifyContent: 'center' is applied.
Here's UPDATE 1 example.

With minimal modification it works for me, I used this style:
const styles = StyleSheet.create({
screen: {
backgroundColor: 'lightgray',
flex: 1,
},
header: {
flex: 0.33,
backgroundColor: 'lightblue',
justifyContent: 'center',
alignItems: 'center',
},
scrollView: {
flex: 1,
},
footer: {
backgroundColor: 'lightpink',
flex: 0.33,
alignItems: 'center',
justifyContent: 'center',
},
});
Full code >>> https://snack.expo.io/Hk9tbHR4Q
This is the result:

Related

React Native - can't scroll inside ScrollView wrapped inside animated view on Android

I have a <ScrollView />, wrapped inside an Animatable.View (a wrapper for Animated.View with a few standard animations)
Like this:
<Animatable.View
animation={this.state.businessSlide}
duration={500}
delay={this.state.delay}
useNativeDriver={false}
easing={"ease-in-cubic"}
onAnimationEnd={this.handleBusinessPop}
>
<ScrollView
style={{ width: "100%", alignSelf: "center", height: 175, zIndex: 999 }}
contentContainerStyle={{ alignSelf: "center", justifyContent: "center", alignItems: "center", flexGrow: 1 }}
onScroll={(event) => this.businessScroll(event)}
scrollEventThrottle={16}
>
{this.state.businessMerchants.map((merchant, index) => (
<Business
merchant={merchant}
key={merchant.id}
isCurrentItem={index === this.state.currentItemIndex}
/>
))}
<View style={{ height: 100 }} />
</ScrollView>
</Animatable.View>
The view slides in from the bottom the position.
I believe this is due to some sort of overlap or something, as if I set the translate distance to 0, (i.e. it animates to where it would be normally without the animated view), it scrolls fine.
This only happens on Android, iOS works as expected.

Extra blank space on bottom while using KeyboardAwareScrollView - React-native

I am trying to make a form with multiple input fields using react-native. I want the form submit button to remain at the bottom of the screen. I've used the following structure
<KeyboardAwareScrollView
contentContainerStyle={{
backgroundColor: "black",
flexGrow: 1
}}
enableOnAndroid={true}
>
<View style={styles.container}>
<View style={styles.formContainer}>
{/*Form input components here*/}
</View>
<View style={styles.buttonContainer}>
<Button
icon={<Icon name="back" size={24} color="white" />}
onPress={props.setModalVisible}
buttonStyle={styles.backButton}
/>
<Button
buttonStyle={styles.button}
containerStyle={{ flex: 1 }}
title="Update Info"
onPress={props.setModalVisible}
/>
</View>
</View>
</KeyboardAwareScrollView>
The container has style
container: {
padding: 10,
backgroundColor: "green",
flex: 1
},
The form container has style
formContainer: {
padding: 15,
flex: 1,
backgroundColor: "blue"
},
Button container has style
buttonContainer: {
padding: 15,
flexDirection: "row",
backgroundColor: "yellow",
marginTop: 'auto'
},
Using the above code the button sticks to the bottom when there is no keyboard as I expected. I've added background color for easy visualization. Image -
Form View
But when I click on a text field and scroll up I see that the container "shrinks" and the bottom of the screen is black and button is now close to input fields. Background color of KeyboardAwareScrollView contentContainerStyle fills the bottom when I [fully scroll to bottom][3]
How do I make sure that the button still sticks to the bottom in scrollview and the content container height remains the same as screen height?
I am using "react-native-keyboard-aware-scroll-view": "0.8.0",
I've tried removing marginTop: 'auto' from the buttonContainer, fixing the height of the container and all sorts of combinations with flexGrow and flex on KeyboardAwareScrollView.

How do I make a React Native Image component adjust view bounds on Android?

I have the following Image component paired with two Text elements in a React Native flexbox:
<View
style={{
flex: 1,
flexDirection: 'row',
}}>
<Image
style={{
width: 100,
height: undefined,
margin: 4,
// borderWidth: 1,
// borderColor: '#777',
}}
source={{uri: image.url}}
resizeMode='center'
/>
<View style={{flex: 1, flexDirection: 'column', justifyContent: 'flex-start'}}>
<Text style={styles.title}>{article.promotionContent.title.value}</Text>
<Text style={styles.description}>{article.promotionContent.description.value}</Text>
</View>
</View>
It porduces this output when implemented as above:
However I suspect that it doesn't adjust view bounds since I get white space above the image (it's supposed to align to the top) and if I add borders to the View, i.e. uncommenting the two lines, I get this weird artifact:
Have anyone else experienced this?
If you want to test my project, check out commit 3a3f705c271a0b523a1769536d16984c5dd47233 in this repo
My question is somewhat laden with assumptions. My end goal is to rescale height after having set a constant width, add border and top align.

React Native Android - how do i *stop* <View>'s from resizing automatically when inside a Modal?

version of RN is 0.41.2
does anyone know how to stop View's that use flexbox for sizing, from automatically "shrinking" their height if a TextInput element is used in one of the Views? It's probably easier to illustrate by example, and to be clear, you can see that these are View's nested inside a <Modal>.
Here is the view when no keyboard is open. Same on both.
Here's what happens when TextInput has the focus. I dont want these views to adjust as they have, above the keyboard. I want the yellow and blue colored View's to remain 'full size' - exactly as illustrated in the iOS screenshot.
This same code, on iOS, does not move/adjust the View's (above the keyboard). That's the behavior I want on Android too.
Here is the sample render method code. It's just a standard template RN project with a change to the render method to test this out.
I tried inserting android:windowSoftInputMode="adjustNothing" into AndroidManifest.xml but had no effect. I'm sure it's just a prop or other manifest setting. Hoping someone can let me know?
render() {
return (
<View style={styles.container}>
<Modal
animationType={'slide'}
transparent={false}
onRequestClose={() => console.log('sd')}
>
<View style={{flex:1, backgroundColor: 'lightgrey'}}>
<View style={{
//height:300,
flex: 1,
backgroundColor: 'yellow',
alignItems: 'center',
justifyContent: 'center',
}}>
<View style={{
margin: 10,
//height:100,
width: 200,
justifyContent: 'center',
backgroundColor: 'green'}}>
<TextInput style={{height: 40, backgroundColor: 'orange'}} />
</View>
</View>
<View style={{
//height:200,
flex: 1,
backgroundColor: 'blue',
}}>
</View>
</View>
</Modal>
</View>
);
}
Did you try wrapping all of the contents within a ScrollView and then a child KeyboardAvoidingView?
I had exactly the same problem, and instead of adding ScrollView everywhere I just made a little change in AndroidManifest.xml:
instead of: android:windowSoftInputMode="adjustResize"
i wrote: android:windowSoftInputMode="adjustPan"
Worked like a charm for me

RefreshControl 'stealing' move gesture from ViewPagerAndroid

If I have a ViewPagerAndroid inside of a ScollView with RefreshControl enabled, the RefreshControl will steal the pan movement from the ViewPagerAndroid.
What I mean is, by dragging the ViewPager to the left midway and then, without releasing the touch, start to drag down, the RefreshContorl will activate, appearing and resetting the state of the ViewPager.
I tried to disable the RefreshControl as soon as the ViewPager returns something other than idle, but if the swipe is quick enough (which usually is), the update does not happen on time to stop the RefreshControl from appearing.
My layout is as simple as possible:
<View style={styles.container}>
<ScrollView style={{ flex: 1 }} removeClippedSubviews={true}>
<View style={{ height: 500, width: 350, marginBottom: 15, backgroundColor: 'cyan' }}>
<ViewPagerAndroid style={{ flex: 1 }}>
<View><Text style={{ fontSize: 30 }}>{'Page1'}</Text></View>
<View><Text style={{ fontSize: 30 }}>{'Page2'}</Text></View>
</ViewPagerAndroid>
</View>
<View style={{ height: 500, width: 350, marginBottom: 15, backgroundColor: 'lightblue' }}>
<ViewPagerAndroid style={{ flex: 1 }}>
<View><Text style={{ fontSize: 30 }}>{'Page1'}</Text></View>
<View><Text style={{ fontSize: 30 }}>{'Page2'}</Text></View>
</ViewPagerAndroid>
</View>
</ScrollView>
</View>
I'm using a Mac, react-native version 0.27.2
You're having ViewPagerAndroid in ScrollView, it should be the other way round.
btw. I'm using scrollable tabview with refreshcontrol and it works well. It doesn't use ViewPagerAndroid, as far as I know, but it's crossplatform and performs well (after disabling dev mode and reloading).

Categories

Resources