'KeyboardAvoidingView not Working Properly
KeyboardAvoidingView not Working Properly
I am trying to use the KeyboardAvoidingView with behavior="padding"
.
For some reason, when I'm trying to enter any text in TextInput
, there's a space below the TextInput. Attached is a picture of what is happening as well as the code. Any chance anyone has any idea whats happening here?
render() {
return (
<KeyboardAvoidingView style={{ flex: 1}} behavior="padding">
< View
style={{
flex: 1,
backgroundColor: "#FFFFFF",
}}
>
<ScrollView
contentContainerStyle={{ justifyContent: "flex-end", flex: 1 }}>
<ChatInfo />
</ScrollView>
<View style={styles.container}>
<TextInput
style={styles.input}
underlineColorAndroid="transparent"
autoCapitalize="none"
onChangeText={text => this.setState({ text: text })}
value={this.state.text}
/>
<TouchableOpacity
style={styles.submitButton}
onPress={this.submitName}
>
<Text style={styles.submitButtonText}> SEND </Text>
</TouchableOpacity>
</View>
</ View>
</KeyboardAvoidingView>
);
}
}
export default connect()(ChatScreen);
const styles = StyleSheet.create({
input: {
margin: 2,
paddingLeft: 15,
flex: 1,
height: 40,
padding: 10,
fontSize: 14,
fontWeight: "400"
},
container: {
borderTopWidth: 1,
minWidth: "100%",
borderColor: "#cccccc",
height: 44,
flexDirection: "row",
justifyContent: "space-between",
backgroundColor: "#fff"
},
submitButtonText: {
color: "#0a9ffc",
fontSize: 14,
fontWeight: "500"
},
submitButton: {
backgroundColor: "#fff",
padding: 10,
margin: 2,
height: 40,
alignItems: "center",
justifyContent: "center"
}
});
Solution 1:[1]
If you are using react-navigation, this is affected by the header of the react-navigation. The height of the header is vary on different mobile screen. So you have to get the height of the header and pass into the keyboardVerticalOffset props.
import { Header } from 'react-navigation-stack';
<KeyboardAvoidingView
keyboardVerticalOffset = {Header.HEIGHT + 20} // adjust the value here if you need more padding
style = {{ flex: 1 }}
behavior = "padding" >
<ScrollView>
<TextInput/>
<TextInput/>
<TextInput/>
<TextInput/>
<TextInput/>
<TextInput/>
</ScrollView>
</KeyboardAvoidingView>
Solution 2:[2]
This is a known issue with KeyboardAvoidingView and Android. There are multiple ways to address this issue.
React Native documentation says:
Android may behave better when given no behavior prop at all, whereas iOS is the opposite.
So, if you are working only with Android you may remove behavior prop and it should work straight away. For best results add android:windowSoftInputMode="adjustResize"
to your Manifest.
Alternatively you can give an offset value that works for you something like this:
KeyboardAvoidingView keyboardVerticalOffset={-500} behavior="padding"
For ios do the same thing conditionally:
behavior= {(Platform.OS === 'ios')? "padding" : null}
keyboardVerticalOffset={Platform.select({ios: 0, android: 500})}
Solution 3:[3]
WARNING
This appears to be only a partial solution, although it works initially, if the android phone is locked on the screen with the keyboard avoiding layout, when you unlock you end up with the extra padding above the keyboard again.
tl;dr
Remove android:windowSoftInputMode="adjustResize"
from the AndroidManifest.xml
Before
...
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize"
>
...
After
...
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
>
...
Why
If I understand the issue correctly, I have been dealing with the same thing. By having android:windowSoftInputMode="adjustResize"
in the manifest, the android system will try to do the same job as the KeyboardAvoidingView
. This results in extra spacing being added above the keyboard on Android only.
If working on both platforms you are going to have to deal with this on iOS every time you are working with keyboard input, so best to remove the android specific behaviour by android:windowSoftInputMode="adjustResize"
from the manifest and using the KeyboardAvoidingView
every time.
Solution 4:[4]
The KeyboardAvoidingView
must be a ScrollView
child, not the other way around. This way it behaves normal(normal for what purpose I am using it). Try it and let me know how it went.
<ScrollView>
<KeyboardAvoidingView styles={styles.container} behavior='padding'>
</KeyboardAvoidingView>
</ScrollView>
Solution 5:[5]
For anyone coming here in 2021, a few things for an updated answer:
useHeaderHeight
is no longer exported by@react-navigation/stack
, it is in@react-navigation/elements
.React Native also now recommends setting the
behavior
prop for both iOS and Android. So the full solution would be:
import { useHeaderHeight } from '@react-navigation/elements';
export const MyComponent = () => {
const headerHeight = useHeaderHeight();
return (
<KeyboardAvoidingView
keyboardVerticalOffset={headerHeight}
style={style.container}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
{/* rest of your component */}
</KeyboardAvoidingView>
);
}
I didn't have to add any additional value to headerHeight
in keyboardVerticalOffset
, and it's working great on iOS and Android.
EDIT: The problem now goes deeper, as KeyboardAvoidingView
does not support all types of Android keyboards. The solution must rely on opening the keyboard to a specific element then (for example a text input). This custom component would look like this:
import React, { PropsWithChildren, useEffect, useState } from 'react';
import { Platform, Animated, Dimensions, Keyboard, KeyboardAvoidingView, StyleSheet, TextInput } from 'react-native';
import {useHeaderHeight} from '@react-navigation/elements';
import { useKeyboard } from '@react-native-community/hooks';
export default function KeyboardShift (props: PropsWithChildren<{}>) {
const [shift, setShift] = useState(new Animated.Value(0))
const keyboard = useKeyboard()
// On mount, add keyboard show and hide listeners
// On unmount, remove them
useEffect(() => {
Keyboard.addListener('keyboardDidShow', handleKeyboardDidShow);
Keyboard.addListener('keyboardDidHide', handleKeyboardDidHide);
return () => {
Keyboard.removeAllListeners('keyboardDidShow');
Keyboard.removeAllListeners('keyboardDidHide');
}
}, [])
const handleKeyboardDidShow = () => {
const { height: windowHeight } = Dimensions.get('window');
const keyboardHeight = keyboard.keyboardHeight;
const currentlyFocusedInputRef = TextInput.State.currentlyFocusedInput();
currentlyFocusedInputRef.measure((x, y, width, height, pageX, pageY) => {
const fieldHeight = height;
const fieldTop = pageY;
const gap = (windowHeight - keyboardHeight) - (fieldTop + fieldHeight);
if (gap >= 0) {
return;
}
Animated.timing(
shift,
{
toValue: gap,
duration: 1000,
useNativeDriver: true,
}
).start();
})
}
const handleKeyboardDidHide = () => {
Animated.timing(
shift,
{
toValue: 0,
duration: 1000,
useNativeDriver: true,
}
).start();
}
const { children } = props;
// Android: we need an animated view since the keyboard style can vary widely
// And React Native's KeyboardAvoidingView isn't always reliable
if (Platform.OS === 'android') {
return (
<Animated.View style={[styles.container, { transform: [{translateY: shift}] }]}>
{children}
</Animated.View>
);
}
// iOS: React Native's KeyboardAvoidingView with header offset and
// behavior 'padding' works fine on all ios devices (and keyboard types)
const headerHeight = useHeaderHeight();
return (
<KeyboardAvoidingView
keyboardVerticalOffset={headerHeight}
style={styles.container}
behavior={'padding'}>
{children}
</KeyboardAvoidingView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});
Yes, it's unfortunately long-winded, but it gets the job done for all types of phones on both iOS and Android.
Read more about it here.
SECOND EDIT: As of April 22nd, 2022, I've replaced the complex solution above the following:
Step 1:
behavior={Platform.OS === "ios" ? "padding" : undefined}
Step 2:
Be sure to REMOVE
android:windowSoftInputMode="adjustPan"
(Or any windowSoftInputMode
for that matter) from your AndroidManifest.xml
file.
So far this solution has been working as well (and is way less code - always better, right?)
Solution 6:[6]
I think this is because the behavior props value, so I think adding this line in the keyboardavoidview will help
<KeyboardAvoidingView
style = {{ flex: 1 }}
behavior={Platform.OS === "ios" ? "padding" : null}>
</KeyboardAvoidingView>
Solution 7:[7]
The main issue with KeyboardAvoidingView is that; the understanding of how it works is missing.
I found below link very helpful
https://medium.com/@nickyang0501/keyboardavoidingview-not-working-properly-c413c0a200d4
- First thing, use of flex:1 in KeyboardAvoidingView and behavior: 'padding'
- Next is use of flex:1 in "MainView" which needs to go inside KeyboardAvoidingView
- Last is adding justifyContent: "flex-end" to ""MainView""
Hope it helps
Solution 8:[8]
For React Native Navigation v5, you can use the following:
import { useHeaderHeight } from '@react-navigation/stack';
...
const Component = () => (
<KeyboardAvoidingView
keyboardVerticalOffset={ useHeaderHeight() } // <-- for v5
behavior="padding"
style={{ flex: 1 }}
>
<TextInput
style={{ height: 30, width: "100%, borderWidth: 1 }}
/>
</KeyboardAvoidingView
)
https://reactnavigation.org/docs/stack-navigator#headertransparent
Solution 9:[9]
Many answers here have shown a conditional behavior prop value. Like this.
// incorrect ?
<KeyboardAvoidingView
style = {{ flex: 1 }}
behavior={Platform.OS === "ios" ? "padding" : null}>
</KeyboardAvoidingView>
But this sets the behavior
prop to null on Android.
Android and iOS both interact with this prop differently.
Android may behave better when given no behavior prop at all, whereas iOS is the opposite.
Conditional spreading the behavior
prop provides an exact solution.
It adds the prop on iOS and leaves it out on Android.
// correct ?
<KeyboardAvoidingView
style = {{ flex: 1 }}
{...(Platform.OS === 'ios' && { behavior: 'padding' })}
</KeyboardAvoidingView>
And here's a solution if using styled-components
that doesn't use an unnecessary KeyboardAvoidingView on Android.
import { KeyboardAvoidingView as Kav, Platform, View } from 'react-native';
import styled from 'styled-components/native';
// If ios we change the component type and, via the `attrs` method, add a behavior prop. This
// approach leaves Android alone. Because it already works.
export const ScreenContainer = styled(Platform.OS === 'ios' ? Kav : View).attrs({
behavior: Platform.OS === 'ios' && 'padding',
})`
flex: 1;
`;
Solution 10:[10]
<KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : null}
style={{flex: 1 }}>
In the above snippet, flex is set to 1, which renders the text Input field just above the keyboard by default even when the keyboard isn't opened. And, when the keyboard pops up, the input field is further pushed up by a factor of keyboard height's offset.
Setting flex to 0 shall fix the issue, as it did in my case.
<KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : null}
style={{flex: 0 }}>
<View>
...
</View>
</KeyboardAvoidingView>
Solution 11:[11]
UPDATE: 2021(October)
If you are using react-navigation(currently, I'm using v6), import useHeaderHeight hook(require additional dependencies)
If you want to use ScrollView with KeyboardAvoidingView, you have to wrap ScrollView inside KeyboardAvoidingView(see example below)
NOTE: If your screen have bottomTabsNavigation, be sure to DISABLE on iOS platform tabBarHideOnKeyboard: Platform.OS !== 'ios'
const headerHeight = useHeaderHeight();
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={{flex: 1}}
keyboardVerticalOffset={headerHeight}>
<ScrollView style={{flex: 1}} contentContainerStyle={{flexWrap: 1}}>
...content
</ScrollView>
</KeyboardAvoidingView/>
Solution 12:[12]
<KeyboardAvoidingView styles={styles.container} behavior = 'padding' enabled>
<ScrollView>
<View>
....
</View>
</ScrollView>
</KeyboardAvoidingView>
Solution 13:[13]
I think the best approach is to create a HOC for this, in addition ,by using getBottomSpace from react-native-iphone-x-helper you can solve overlapping issue for IPhone X and..
import React, { ComponentType, ReactNode } from 'react';
import { Platform, KeyboardAvoidingView, View, Pressable, Keyboard } from
'react-native';
import { getBottomSpace } from 'react-native-iphone-x-helper';
interface IProps {
children: ReactNode;
}
const KeyboardAvoidingViewHoc = (Component: ComponentType) => {
return ({ children, ...props }: IProps) => {
return (
<KeyboardAvoidingView {...props} keyboardVerticalOffset=
{getBottomSpace()} behavior= {Platform.OS === 'ios' ? 'padding' : undefined}>
<Pressable onPress={Keyboard.dismiss}>
<Component {...props}>{children}</Component>
</Pressable>
</KeyboardAvoidingView>
);
};
};
export const AvoidKeyboardAvoidingViewHoc = KeyboardAvoidingViewHoc(View);
Solution 14:[14]
After applying ScrollView
the problem was solved for me.
Solution 15:[15]
A handy alternative when wanting to avoid the keyboard with a ScrollView
is the popular react-native-keyboard-aware-scroll-view
package:
https://github.com/APSL/react-native-keyboard-aware-scroll-view
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'
<KeyboardAwareScrollView>
<View>
<TextInput />
</View>
</KeyboardAwareScrollView>
Solution 16:[16]
My issue was with the keyboardHidesTabBar
option. The following setup worked for me:
const AppBottomTabNavigator = createBottomTabNavigator(
{
...
},
{
tabBarOptions: {
keyboardHidesTabBar: Platform.OS !== 'ios',
},
},
);
Component:
import React from 'react';
import {
Keyboard,
KeyboardAvoidingView,
Platform,
StyleSheet,
Text,
TextInput,
TouchableWithoutFeedback,
View,
} from 'react-native';
import { Header } from 'react-navigation-stack';
const styles = StyleSheet.create({
container: {
flex: 1,
},
center: {
justifyContent: 'center',
alignItems: 'center',
},
textInput: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
},
});
const MyScreen = () => {
return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : null}
keyboardVerticalOffset={Header.HEIGHT}
style={styles.container}
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.container}>
<View style={[styles.container, styles.center]}>
<Text>Hello!</Text>
</View>
<TextInput style={styles.textInput} placeholder="Message" />
</View>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
);
};
export default MyScreen;
Solution 17:[17]
My problem was not checking this platform type
adding the code below to KeyboardAvoidView
fixed it for me
behavior={Platform.OS === "ios" ? "padding" : 'height'}
Solution 18:[18]
Although Its not an right answer but there is very popular library for solving this type of issues is there called where it can use scrollview with the sense of keyboard area available.You can go through the below link https://www.npmjs.com/package/react-native-keyboard-aware-scrollview
Solution 19:[19]
I had a similar issue because I'm using @react-navigation
with bottom tabs.
Starting with "@react-navigation/bottom-tabs": "^5.11.2"
you can get the height of the bottom tab bar with one of the following two ways (API reference):
import { BottomTabBarHeightContext } from '@react-navigation/bottom-tabs';
// ...
<BottomTabBarHeightContext.Consumer>
{tabBarHeight => (
/* render something */
)}
</BottomTabBarHeightContext.Consumer>
or
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
// ...
const tabBarHeight = useBottomTabBarHeight();
and then you set it as offset to your KeyboardAvoidingView
:
<KeyboardAvoidingView keyboardVerticalOffset={tabBarHeight} behavior={Platform.OS === "ios" ? "padding" : null} style={styles.container}>
// ...
</KeyboardAvoidingView>
Solution 20:[20]
Keep in mind is always your top Parent with flex:1 then the child is then you text input container if you use this method it work always its test method.
<KeyboardAvoidingView style={{ flex: 1 }}
behavior={Platform.OS === "ios" ? "position" : null} enabled>
<ScrollView>
<View>
<View >
<Text maxFontSizeMultiplier={1.5} >
Sign in to your account{" "}
</Text>
<View
behavior="padding"
enabled
>
<TextInput
placeholder="Email address"
placeholderTextColor={Colors.grey}
style={styles.textInput}
onChangeText={(e) => setEmail(e.trim())}
autoCapitalize="none"
returnKeyType={"done"}
/>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
Solution 21:[21]
import { HeaderHeightContext } from "react-navigation-stack"; import { ..., KeyboardAvoidingView, } from "react-native";
<HeaderHeightContext.Consumer>
{(headerHeight) => (
<KeyboardAvoidingView
{...(Platform.OS === "ios" && {
behavior: "padding",
keyboardVerticalOffset: headerHeight
})}
style={{ flex: 1 }}>
code is here
</KeyboardAvoidingView>
Solution 22:[22]
<KeyboardAvoidingView style={styles.keyboardcontainer} behavior="padding"
keyboardVerticalOffset={Platform.select({ios :120, android : 500})}
enabled>
<View style={{flex: 1 }}>
// Your Code
</View>
</KeyboardAvoidingView>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow