I'm using React Native's Keyboard Avoiding View with the behavior set to padding (testing on Android).
I have multiple TextInputs on my screen. When I click the final TextInput, the keyboard covers it. I am now able to scroll down due to padding added from KeyboardAvoidingView, but it would be ideal to have it auto scroll on focus.
<Content>
<KeyboardAvoidingView behavior='padding'>
<TextInput placeholder='Example 1' />
<TextInput placeholder='Example 2' />
<TextInput placeholder='Example 3' />
<TextInput placeholder='Example 4' />
<TextInput placeholder='Example 5' />
<TextInput placeholder='Example 6' />
<TextInput placeholder='Example 7' />
</KeyboardAvoidingView>
</Content>
there is prop called keyboardVerticalOffset that you can pass to the KeyboardAvoidingView that will change how much the keyboard moves past the textInput.
Sample of my code:
const keyboardVerticalOffset = Platform.OS === 'ios' ? 40 : 0
return (
<KeyboardAvoidingView behavior='position' keyboardVerticalOffset={keyboardVerticalOffset}>
<ListView .../>
<KeyboardAvoidingView/>
)
Depending on platform, Android or IOS, implementation can be vary a little. This is how I did.
Add android:windowSoftInputMode="adjustResize" at AndroidManifest.xml,
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:label="#string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize">
</activity>
In your container
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : null}
keyboardVerticalOffset={Platform.OS === "ios" ? 64 : 0}>
<ScrollView>
{...}
</ScrollView>
</KeyboardAvoidingView>
keyboardVerticalOffset tells how much the keyboard moves past the textInput.
react-native-keyboard-aware-scroll-view
It's super simple to use and it worked great in both Android and iOS.
It supports older versions of RN too.
Initially I tried the KeyboardAvoidingView but on IOS not even
behavior='position' with keyboardVerticalOffset worked properly.
That used to overlap some content in a strange way.
I have:
RN 0.53.3
react-native-keyboard-aware-scroll-view 0.6.0
I added a few more details about my use case here:
https://stackoverflow.com/a/51151496/1979861
To add to #Maxwell's answer, sometimes you may need to scroll further than the end of the scroll view to get a component into view, since the added padding is not the full height of the keyboard. Full example below using scrollTo() with y offset as the height of the text input.
import React, { Component } from 'react'
import {
KeyboardAvoidingView,
ScrollView,
View,
TextInput
} from 'react-native'
export default class Test extends Component {
render() {
return (
<ScrollView style = {{flex:1, backgroundColor: 'white'}} ref = 'scroll'>
<KeyboardAvoidingView behavior='position' style = {{backgroundColor: 'white', flex: 1}}>
<View style = {{height: 400}}/>
<TextInput style = {{height: 60}} placeholder='Example 1' />
<TextInput style = {{height: 60}} placeholder='Example 2' />
<TextInput style = {{height: 60}} placeholder='Example 3' />
<TextInput style = {{height: 60}} placeholder='Example 4' onFocus = {() => this.refs['scroll'].scrollTo({y: 60})}/>
</KeyboardAvoidingView>
</ScrollView>
)
}
}
based on #Richard Millen something change in this styles
<ScrollView
contentContainerStyle={{
flexGrow: 1,
padding: 20
}}
>
<TextInput
style = {{ minHeight: 100 }}
/>
<TextInput
style = {{ minHeight: 100 }}
/>
...
</ScrollView>
if you are using react-navigation v6 you might need
import { useHeaderHeight } from "#react-navigation/elements";
const headerHeight = useHeaderHeight();
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : undefined}
style={flexGrow}
keyboardVerticalOffset={Platform.OS === "ios" ? headerHeight + Constants.statusBarHeight : 0}
>
</KeyboardAvoidingView>
Related
i got an problem with the refreshing on pull function. The FlatList renders fine, but pull to refresh is not working. This is my current sourcecode:
return (
<View style={GlobalStyles.flex1}>
<FlatList
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
refreshing={isRefreshing}
onRefresh={() => {
console.log("onRefresh loadVocable");
loadVocables();
}}
/>
}
data={vocables}
keyExtractor={vocable => vocable.id}
onEndReached={() => {
if (!isRefreshing && !endReached) {
loadVocables();
}
}}
renderItem={vocable => (
<TouchableOpacity
onPress={() => {
props.navigation.navigate({ routeName: "editVocable", params: { vocable: vocable.item } });
}}
onLongPress={() => {
handleLongPress(vocable.item.id);
}}>
<Card style={styles.cardStyle}>
<View style={styles.part1}>
<Text style={styles.vocableText}>{vocable.item.wordENG}</Text>
<Text style={styles.vocableText}>{vocable.item.wordDE}</Text>
</View>
<View style={styles.part2}>
<Ionicons name={vocable.item.known ? "md-checkmark-circle" : "md-close-circle"} size={23} color={vocable.item.known ? Colors.success : Colors.danger} />
</View>
</Card>
</TouchableOpacity>
)}
/>
</View>
);
In the official docs is an example that says contentContainerStyle needs to be flex: 1 to know the height, that makes sence to me, so when i set contentContainerStyle with flex 1, refresh on pull works fine, but then i can't scroll anymore in the Flatlist and everthing get very tight, so the style also change then. Does anyone know why this happen?
The first picture is with "contentContainerStyle={{flex: 1}}" and the last one is without contentContainerStyle.
The answer was so easy, I compared a new project (there worked my code) to the one where the problem was and after 5 days I found the little error:
My import was wrong!
I imported FlatList like this:
import { FlatList } from "react-native-gesture-handler";
But it needs to get imported from React-Native so like this:
import { FlatList } from "react-native";
Thanks to #The1993, without the hint to compare the projects, maybe I would stuck forever on this error :D In the future I will compare working files to find any error!
contentContainerStyle is used to style inner content e.g items alignments, padding, etc
style is used to align its height and relations
You can replace style={{flex: 1}} instead of contentContainerStyle or wrap the parent element with flex: 1
I have multiple Views inside my horizontal ScrollView. How can I set them such that each of those views has the exact dimensions as that of the ScrollView?
I want to use paging and go to my next View inside the ScrollView on swiping. I have tried to use width : "100%" but it does not work as ScrollView does not have fixed dimensions, but that is basically what I want to do with the views inside it. Here is my code:
export default class HorizontalScrollView extends Component {
render () {
screenWidth = "100%"
return (
<ScrollView
contentContainerStyle={styles.scrollable}
horizontal={true}
snapToInterval={screenWidth}
decelerationRate={"fast"}
>
<View style={StyleSheet.compose(styles.insideContainer, {"width" : screenWidth, "backgroundColor" : "red"})}>
<Text>Screen 1</Text>
</View>
<View style={StyleSheet.compose(styles.insideContainer, {"width" : screenWidth, "backgroundColor" : "blue"})}>
<Text>Screen 3</Text>
</View>
<View style={StyleSheet.compose(styles.insideContainer, {"width" : screenWidth, "backgroundColor" : "green"})}>
<Text>Screen 2</Text>
</View>
</
StyleSheet:
styles = StyleSheet.create({
"scrollable" : {
"backgroundColor" : "black",
"height" : "100%"
},
"insideContainer" : {
"alignItems" : "center",
"justifyContent" : "center",
"flex" : 1,
}
})
P.S. - I am using an Android Samsung Galaxy M30 to test this.
Any help or explanation would be appreciated.
Thank you.
Based on your code use these styles to accomplish what you want
<ScrollView horizontal={true} snapToInterval={Dimensions.get('screen').width} contentContainerStyle={{minHeight: '100%'}}>
<View style={{flexDirection: 'row'}}>
<View style={{width: Dimensions.get('screen').width, height: Dimensions.get('screen').height, backgroundColor: 'green'}}>
<Text>Screen 1</Text>
</View>
<View style={{width: Dimensions.get('screen').width, height: Dimensions.get('screen').height, backgroundColor: 'red'}}>
<Text>Screen 2</Text>
</View>
<View style={{width: Dimensions.get('screen').width, height: Dimensions.get('screen').height, backgroundColor: 'yellow'}}>
<Text>Screen 3</Text>
</View>
</View>
</ScrollView>
If your scroll view is the width of the screen, you can set each view inside to have a width of
Dimensions.get(“screen”).width
Then set the pagingEnabled prop of the ScrollView to true and you should have a paging scroller with full width sub views.
I am trying to make a 1:1 chat, however when I try to tap on the TextInput the KeyboardAvoidingView is not working, I am not sure if this is something with my styles or if I am not using it correctly, here you can find my snack
And this is the code:
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
placeholder: 'Write a message...',
text: ''
};
}
render() {
return (
<KeyboardAvoidingView style={{flex: 1, marginTop: 20}} behavior="padding" enabled>
<View style={styles.messageView}>
<Message
message="Hi! First Message"
styleMessage={'one'}
/>
<Message
message="Hi! Second Message"
styleMessage={'two'}
/>
</View>
<View style={styles.container}>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
selectionColor={BLUE}
placeholderTextColor={LIGHT_GRAY}
onChangeText={(text) => this.setState({text})}
placeholder={this.state.placeholder}
value={this.state.text}
onEndEditing={() => {console.log(1+1)}}
/>
</View>
<TouchableHighlight style={styles.sendButton}>
<Ionicons name="md-send" size={25} color='#FFFFFF' style={{textAlign: 'center'}}/>
</TouchableHighlight>
</View>
</KeyboardAvoidingView>
);
}
}
in this Context you only need to change
behavior="padding"
for
behavior="height"
and it will starts working correctly on both plataforms (Android/iOS)
Personally I recommend you to use instead KeyboardAwareScrollView from react-native-keyboard-aware-scroll-view package (Link) because it let you configure easier and faster the behavior of it.
I am currently trying to use a <SectionList> inside a classic <View>, all my datas are formatted, the list displays correctly and my item's actions are working.
The issue is that when I am on the top of my SectionList, the area available to trigger the scroll is actually really small ( roughly 100 pixels from the top of the list ). However, once i scroll down a bit from that area, the whole list becomes scrollable and works as intended until I scroll back to the top.
My parent View has flex: 1 as well as my SectionList
Environment
Working environment : MacOS Sierra 10.13.3
Expo version : 23.0
React Native version : 0.50
React : 16.0
Using an IPhone 8 simulation
There's no issue on Android
Steps to Reproduce
Classic creation of a SectionList inside of a View
Expected Behavior
The scroll must be triggered from everywhere in the SectionList
Actual Behavior
When the SectionList is at the top, the scroll only triggers inside a small area ( around 100px from the top of the list )
The code of my SectionList :
<View style={{ flex: 1 }}>
<SectionList
style={styles.openSectionList} // flex: 1
scrollEnabled
stickySectionHeadersEnabled
sections={this.sections}
keyExtractor={item => item["#id"]}
removeClippedSubviews
renderSectionHeader={({ section }) => (
<TouchableHighlight
onPress={() => this.onSectionHeaderPressRef(section.index)}
activeOpacity={0.65}
underlayColor="rgba(0, 0, 0, 0.2)"
style={styles.sectionHeader}
>
<View style={styles.categoryContentContainer}>
<View style={styles.firstPartContent}>
<Text style={styles.categoryHeaderText}>
{section.title === "Autres"
? "Mes produits"
: section.title}{" "}
</Text>
{section.nbItems - section.nbItemsSelected === 0 ? (
<CheckBox
label=""
checked
checkboxStyle={styles.checkbox}
checkboxContainer={styles.checkboxContainer}
/>
) : (
<Text
style={[
styles.categoryHeaderText,
{ color: Colors.contrastColor },
]}
>
({section.nbItems - section.nbItemsSelected})
</Text>
)}
</View>
<Image
source={require("../../../assets/common/chevron.png")}
style={
section.index === this.state.currentCategoryOpen
? styles.categoryChevronOpen
: styles.categoryChevronClosed
}
/>
</View>
</TouchableHighlight>
)}
renderItem={({ item }) =>
this.state.currentCategoryOpen === item.categoryIndex ? (
<ShoppingListProduct
key={item["#id"]}
ingredient={item}
updateIngredient={this.updateIngredientListRef}
onLongPress={this.itemLongPressedRef}
/>
) : null}
/>
</View>
A GIF of the actual behavior ( I'm trying to scroll everytime the cursor is moving ) where we can see that the scroll only triggers when I am above a certain height.
GIF
Any help would be appreciated as I don't know if that's a bug and/or me implementing the component wrong.
Thank you by advance.
someone asked me the solution for this via email so I might as well add it here, from what i remember it was a position/imbrication problem with the components.
I can't remember exactly but I ended up with this code ( my page content changes so that's why it is set as a variable )
// render method
component = ( <SectionList
style={styles.openSectionList} // flex: 1, height: "100%"
scrollEnabled
stickySectionHeadersEnabled
sections={this.sections}
bounces={false}
keyExtractor={item =>
item["#id"] === undefined ? item.userIngredient : item["#id"]
}
getItemLayout={this.getItemLayout}
renderSectionHeader={this.renderSectionListHeaderRef}
renderItem={this.renderSectionListItemRef}
/> )
return (
<View style={{ flex: 1 }}>
{component}
</View>
)
So yeah watch out where your SectionList is defined and how many parents it has, I think it required only one
Hope this helps.
I was able to fix mine by adding this prop to the section list
stickySectionHeadersEnabled={false}
I added a marginBottom to the SectionList of equal amount of space consumed by the View on top to equalise the area.
Is there any way to get height of whole ScrollView element in React Native?
return (
<View ref='container' style={[ styles.container, backgroundColor, {width: this.props.width} ]}>
<ScrollView ref='scroll' style={styles.scrollView}> --> what height with inner content?
{this._getTitle()}
<View style={styles.content}>
{items}
</View>
<View style={[styles.empty]} />
</ScrollView>
</View>
)
I don't know if this is an officially documented API, but getting the content size can be done using the native scroll-view manager.
Add the necessary imports:
import ReactNative, {NativeModules} from 'react-native';
const ScrollViewManager = NativeModules.ScrollViewManager;
Call this in your code at a point where you already have access to the scrollview ref:
if(ScrollViewManager && ScrollViewManager.getContentSize) {
ScrollViewManager.getContentSize(ReactNative.findNodeHandle(this.scrollViewRef), (contentSize) => {
console.log(contentSize);
})
}