In React Native, How Can I Use KeyboardAvoidingView with a Modal in iOS?

I am having trouble getting a KeyboardAvoidingView to work in iOS, specifically when wrapping a Modal in it. Testing in Android, the Modal avoids the keyboard correctly, but in iOS the keyboard covers the Modal.

Here is a reproducible example, using an iPhone X as my test device:

import React, {useState} from 'react';
import {StyleSheet, ScrollView, View, Modal, TextInput, KeyboardAvoidingView, Button, SafeAreaView} from 'react-native';

export default function App() {
    const [modalVisible, setModalVisible] = useState(false);

    return (
        <View style={styles.container}>
            <Button title={"Open Modal"} onPress={() => setModalVisible(true)}/>
            {modalVisible && 
            <KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : "height"}>
                <Modal
                    animationType="slide"
                    transparent={true}
                    visible={modalVisible}>
                    <SafeAreaView style={styles.safeAreaView}>
                        <View style={styles.modalContentContainer}>
                            <Button title={"Close Modal"} onPress={() => setModalVisible(false)}/>
                            <ScrollView>
                                <View style={styles.textInputContainer}>
                                    <TextInput
                                        value={"test 1"}
                                        onChangeText={() => {}}
                                        onBlur={() => {}}
                                    />
                                </View>
                                <View style={styles.textInputContainer}>
                                    <TextInput
                                        value={"test 2"}
                                        onChangeText={() => {}}
                                        onBlur={() => {}}
                                    />
                                </View>
                                <View style={styles.textInputContainer}>
                                    <TextInput
                                        value={"test 3"}
                                        onChangeText={() => {}}
                                        onBlur={() => {}}
                                    />
                                </View>
                                <View style={styles.textInputContainer}>
                                    <TextInput
                                        value={"test 4"}
                                        onChangeText={() => {}}
                                        onBlur={() => {}}
                                    />
                                </View>
                            </ScrollView>
                        </View>
                    </SafeAreaView>
                </Modal>
            </KeyboardAvoidingView>}
        </View>
    );
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'center',
        paddingTop: 50
    },
    safeAreaView: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
    },
    modalContentContainer: {
        margin: 100,
        backgroundColor: "#d6d6d6",
        width: "80%",
        height: "60%",
        borderRadius: 10,
    },
    textInputContainer: {
        flex: 1,
        margin: 40,
        alignItems: "center",
    }
});

You will notice that when you open the Modal and tap on the last TextInput field, the keyboard comes up and covers the Modal. The Modal does not avoid the keyboard, as would be expected.

Here is a screenshot from my testing in iOS, where it is not working:

iOS not working

And here is a screenshot from my testing in Android, where it is working:

Android working

Any ideas on how I can make a Modal avoid the keyboard in iOS?


Solution 1:

You are wrapping your Modal with a KeyboardAvoidingView. I'm not sure about this but from my testing it seems to be impossible to resize a Modal using a KeyboardAvoidingView.

So move the KeyboardAvoidingView inside of the Modal to get the expected behaviour. Like this:

return (
        <View style={styles.container}>
            <Button title={"Open Modal"} onPress={() => setModalVisible(true)}/>
            {modalVisible && 
                <Modal
                    animationType="slide"
                    transparent={true}
                    visible={modalVisible}>
                      <KeyboardAvoidingView behavior={"padding"} style={styles.safeAreaView}>
                        <View style={styles.modalContentContainer}>
                            <Button title={"Close Modal"} onPress={() => setModalVisible(false)}/>
                            <ScrollView>
                                <View style={styles.textInputContainer}>
                                    <TextInput
                                        value={"test 1"}
                                        onChangeText={() => {}}
                                        onBlur={() => {}}
                                    />
                                </View>
                                <View style={styles.textInputContainer}>
                                    <TextInput
                                        value={"test 2"}
                                        onChangeText={() => {}}
                                        onBlur={() => {}}
                                    />
                                </View>
                                <View style={styles.textInputContainer}>
                                    <TextInput
                                        value={"test 3"}
                                        onChangeText={() => {}}
                                        onBlur={() => {}}
                                    />
                                </View>
                                <View style={styles.textInputContainer}>
                                    <TextInput
                                        value={"test 4"}
                                        onChangeText={() => {}}
                                        onBlur={() => {}}
                                    />
                                </View>
                            </ScrollView>
                        </View>
                    </KeyboardAvoidingView>
                </Modal>}
        </View>
    );

I've also removed your SafeAreaView since it currently doesn't do anything. Your parent is already centered and its content will never reach the 'unsafe' areas of a device.

You might as well remove the {modalVisible && ...} from your code since visible={modalVisible} already hides and shows the modal when needed.

Demo: https://imgur.com/W7sEPpn