react-native-paper
react-native-paper copied to clipboard
Modals on iOS do not respond to the presence of the on-screen keyboard
Current behaviour
On web and Android, the Modal component respects the state of the keyboard, adjusting it's height accordingly. On iOS, however, the Modal does not move up when the keyboard is opened.
This issue is likely related to 2172
Expected behaviour
The Modal component should respect the positions of the keyboard and respond accordingly on all platforms.
How to reproduce?
- Add a Modal component inside a Portal component.
- Add content to the Modal component and open the Modal.
- Open the device on-screen keyboard on an iOS device.
- Notice how the Modal doesn't respond (move up) to accommodate the keyboard, even though there is empty space available above the modal.
Preview
iOS:
Android:
What have you tried so far?
We're currently following the workaround listed here.
Your Environment
software | version |
---|---|
ios | x |
android | x |
react-native | 0.71.14 |
react-native-paper | 5.11.2 |
node | 20.9.0 |
npm or yarn | 10.1.0 |
expo sdk | 48.0.20 |
Having the same issue
@Bi0max please add your issue reproduction
@lukewalczak , hi , it's literally the same as the OP described. It is reproduced just with and empty Modal and an opened keyboard on iOS.
Hello! I confirm the issue. It seems there is a problem with Portal and its keyboard avoiding behavior on iOS. It also makes SnackBar (placed in Portal) appear under the keyboard on iOS. Reproduced it the same way the issue's author described.
https://github.com/callstack/react-native-paper/assets/32746079/49953d6b-45e7-4d0a-9168-d4b5ea6c6b34
My environment
software | version |
---|---|
ios | 17.2 |
android | 13.0 |
react-native | 0.72.10 |
react-native-paper | 5.12.2 |
node | 21.3.0 |
yarn | 1.22.21 |
Same issue after upgrading to latest and placing modal within Portal component.
Same issue, Portal does not response to keybored in IOS even with KeyboardAvoidingView, for anyone who is looking for a solution, you can use Modal from react nativre for IOS with keyboredAvoidView, it works fine
Has there been any update or progress on this?
I have fixed this by patching the react-native-paper
dependency (using yarn
)
Here I share the changes in case it's helpful for others.
There's probably a better approach, but this might suffice while RNP implement an actual fix.
cc @lukewalczak
Please ignore some formatting changes in here
diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx
index 313ecee43bde4a0b6be9cbd13b673452725f0f6e..12a7e3d0494a5e256ba5e22a89d71572df547789 100644
--- a/src/components/Modal.tsx
+++ b/src/components/Modal.tsx
@@ -7,6 +7,8 @@ import {
Pressable,
View,
ViewStyle,
+ KeyboardAvoidingView,
+ Platform,
} from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -177,11 +179,7 @@ function Modal({
return true;
};
- const subscription = addEventListener(
- BackHandler,
- 'hardwareBackPress',
- onHardwareBackPress
- );
+ const subscription = addEventListener(BackHandler, 'hardwareBackPress', onHardwareBackPress);
return () => subscription.remove();
}, [dismissable, dismissableBackButton, hideModal, visible]);
@@ -204,17 +202,16 @@ function Modal({
<Animated.View
pointerEvents={visible ? 'auto' : 'none'}
accessibilityViewIsModal
- accessibilityLiveRegion="polite"
+ accessibilityLiveRegion='polite'
style={StyleSheet.absoluteFill}
onAccessibilityEscape={hideModal}
- testID={testID}
- >
+ testID={testID}>
<AnimatedPressable
accessibilityLabel={overlayAccessibilityLabel}
- accessibilityRole="button"
+ accessibilityRole='button'
disabled={!dismissable}
onPress={dismissable ? hideModal : undefined}
- importantForAccessibility="no"
+ importantForAccessibility='no'
style={[
styles.backdrop,
{
@@ -225,21 +222,20 @@ function Modal({
testID={`${testID}-backdrop`}
/>
<View
- style={[
- styles.wrapper,
- { marginTop: top, marginBottom: bottom },
- style,
- ]}
- pointerEvents="box-none"
- testID={`${testID}-wrapper`}
- >
- <Surface
- testID={`${testID}-surface`}
- theme={theme}
- style={[{ opacity }, styles.content, contentContainerStyle]}
- >
- {children}
- </Surface>
+ style={[styles.wrapper, { marginTop: top, marginBottom: bottom }, style]}
+ pointerEvents='box-none'
+ testID={`${testID}-wrapper`}>
+ <KeyboardAvoidingView
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
+ keyboardVerticalOffset={100}
+ style={styles.keyboardAvoidingView}>
+ <Surface
+ testID={`${testID}-surface`}
+ theme={theme}
+ style={[{ opacity }, styles.content, contentContainerStyle]}>
+ {children}
+ </Surface>
+ </KeyboardAvoidingView>
</View>
</Animated.View>
);
@@ -260,4 +256,10 @@ const styles = StyleSheet.create({
backgroundColor: 'transparent',
justifyContent: 'center',
},
+ keyboardAvoidingView: {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'stretch',
+ justifyContent: 'center',
+ },
});
Here's proof of it working:
iOS
https://github.com/user-attachments/assets/436bde65-f8af-415a-af28-22b0233b9a21
Android
https://github.com/user-attachments/assets/a3f28d21-f95b-481f-9f98-9aab056c5265