-
-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
KeyboardAvoadingView padding calculation on keyboard type change is not correct #672
Comments
Thanks @davoam for raising the issue It's very interesting, that the issue happens only with custom keyboards (i. e. not a stock one). I will have a look on this problem 👀 |
@davoam I think the issue is reproducible with default keyboard as well? See the video below: Screen.Recording.2024-11-03.at.13.12.51.movI think this problem is located in native code. Because let's say if QWERTY keyboard height is For me it looks like it's Android OS bug, but I will see if such problem can be reproduced in native code or can be fixed within this library 👀 |
One potentail solution I'm thinking of is to store a map of key value where key is type of Is it possible to wait for keyboard hide and then trigger input unmount? Or maybe wait for a keyboard to disappear and only when it's hidden request a focus to a new input? Or you can mount a new input, switch focus, and only when focus gets switched then you will remove previous field? What do you think about these approaches? |
Thanks for the quick response, @kirillzyusko ! I tried closing the keyboard, waiting for Mounting new input is also not a good solution since these two inputs may be on different screens. It makes things complex. So, as a temporary hack we used this 1-second delay. We close the keyboard, wait for Here is the code when which works correctly, but if you change import { Button , View, TextInput, StatusBar, Keyboard} from 'react-native';
import { useState, useRef} from 'react';
import { ThemedText } from '@/components/ThemedText';
import { KeyboardAvoidingView, KeyboardStickyView } from 'react-native-keyboard-controller';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
export default function HomeScreen() {
const [isNumberFieldDisplayed, setIsNumberFieldDisplayed] = useState(true);
const insets = useSafeAreaInsets()
const textInputRef = useRef<TextInput | null>(null);
const numberInputRef = useRef<TextInput | null>(null);
const onChangeFocus = () => {
function toggleIsNumberFieldDisplayed() {
setIsNumberFieldDisplayed((prevValue) => {
const nextIsNumberFieldDisplayed = !prevValue
setTimeout(() => {
nextIsNumberFieldDisplayed ? numberInputRef.current?.focus() : textInputRef.current?.focus();
}, 1000);
return nextIsNumberFieldDisplayed;
});
}
if (Keyboard.isVisible()) {
const listener = Keyboard.addListener('keyboardDidHide', () => {
toggleIsNumberFieldDisplayed();
listener.remove();
});
Keyboard.dismiss();
} else {
toggleIsNumberFieldDisplayed();
}
}
return (
<View style={{flex: 1}}>
<KeyboardAvoidingView keyboardVerticalOffset={StatusBar.currentHeight} behavior="padding" style={{flex: 1}}>
<View style={{flex: 1, paddingTop: insets.top}}>
<View style={{flex: 1}}>
<ThemedText type="title">Welcome!</ThemedText>
<View style={{flex: 1, backgroundColor: 'red'}}>
<Button title="Change focus" onPress={onChangeFocus} />
{!isNumberFieldDisplayed && <TextInput key="textInput" ref={textInputRef} placeholder="Type something here" placeholderTextColor="black"/>}
{isNumberFieldDisplayed && <TextInput key="numberInput" ref={numberInputRef} placeholder="Type a number here"placeholderTextColor="black" keyboardType="number-pad"/>}
<View style={{marginTop: 'auto'}}>
<Button title="Footer button" onPress={() => {}} />
</View>
</View>
</View>
</View>
</KeyboardAvoidingView>
</View>
);
} |
@davoam okay interesting - 300ms is a pretty big time interval, so it should work 🤯 I'll have a look again on this problem! It's weird, that you have to wait 1s! |
@davoam what do you think about next API? Just theoretically I can add So in your code you will need to make next modification: <Button
title="Change focus"
onPress={async () => {
await KeyboardController.retain();
setIsNumberFieldDisplayed((prevValue) => !prevValue)}
}
/> And in this case the keyboard will not be automatically hidden, when you unmount/mount new fields. The only one downside that I see is that this method will not be able to restore the internal state of the keyboard. I. e. if you switched to emoji keyboard, or applied force upper case mode or changed something else, then all these preferences will be lost when From the other side you almost instantly change the input type, so such transition should be okay, I believe. Anyway, let me know what do you think about it 🙌 Curious to know your thoughts 😊 |
@kirillzyusko, sorry for the late response. Looks good to me, it will even help to avoid unnecessary animation on keyboard hide/show. Imagine a standard one-time-password (OTP) flow. When on one screen you enter a phone number and on another you enter OTP. When you switch from one screen to another, the keyboard gets hidden and opened again. We can keep showing it with this method. But will it help to have correct height transition and avoid jumping on keyboard type change? |
Yes, the keyboard will be on hold until new input comes in. The keyboard height will be anyway different (because numeric keyboard are smaller than QWERTY keyboards), but I believe there will be an immediate transition and no jumpy/de-synchronized transitions. Anyway, let me try to experiment with your code and with new method and see how it can solve the problem 👀 |
@davoam I did a quick PoC here: https://github.com/kirillzyusko/react-native-keyboard-controller/tree/feat/hold This is how it works: Screen.Recording.2024-12-26.at.18.29.15.movLet me know what do you think about it 🙌 |
@kirillzyusko looks much better! Thanks a lot!
|
This is because I have a header + StatusBar and I used
I think that's what I used before, but then I decided to go back to non animated transition, if there is a non-animated keyboard animation. You can read more details here: #376 However for Note I also discovered that invisible Let me know if you have any further questions 😊 |
Describe the bug
There are two inputs with different keyboard types, and only one is rendered at a time. One input has the default keyboard type, while the other uses 'number-pad'. When we unmount the default text input and show the number input, KeyboardAvoidingView first sets padding as if the default keyboard is displayed, and then adjusts it for the number keyboard.
This transition creates a visible jump that only occurs on Android. In the attached GIF file, you can see that when switching to the numeric keyboard, the footer button is initially rendered higher before moving down.
This issue is easily reproducible with an app created using the latest Expo CLI.
Important: the issue is reproducible with a non-default keyboard. In the example, this keyboard is used. With the default keyboard, everything works as expected.
Code snippet
Expected behavior
Padding is changed smoothly like it does on iOS.
Screenshots
Smartphone (please complete the following information):
The text was updated successfully, but these errors were encountered: