Reputation: 291
I switched laptops and cloned the repository of my project, did a quick yarn install and looks like it was a big difference from the one on the main branch but I didn't bother since maybe it's just because of different Node versions.
Now every time I click on one TextInput the keyboard opens and closes immediately only on android. I attached a quick recording here. Tried some solutions and it looks like switching android:windowSoftInputMode from adjustResize to adjustPan in AndroidManifest.xml fixes the problem with the closing but I'm not really happy with the behaviour of the keyboard in the app when it's set to adjustPan. Maybe this issue starting happening a while ago but I just saw it now.
Here is just an input centered inside a simple View. https://gfycat.com/ordinaryquestionabledinosaur
Any suggestions anyone?
Upvotes: 17
Views: 13364
Reputation: 11
I updated react-native from 0.64.3 to 0.66.5 and it worked like a charm. Maybe it also works with some version of 0.65.x . I'm also using react react-native-screens.
Upvotes: 1
Reputation: 2756
Scroll down for the solution
Okay, so, its been a while since this issue was active, and honestly I find the answers here to be kind of half baked, I have a solution that fixes this problem regardless of react-native-screens
, but the explanation is quite long, so bear with me here.
The solutions suggesting to upgrade the react-native-screens
lib are no longer relevant, I am currently running two projects using [email protected]
and both these projects are experiencing the same broken behavior, expect it only happens in FlatList
s with inputs that are positioned on the lower part of the screen.
So the Upper to middle inputs work fine, but all the inputs that would be placed under the keyboard once it opens, cause the above mentioned weird behavior.
The solutions that suggest to change the windowSoftInputMode
to "stateAlwaysHidden|adjustPan"
, i.e: android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
.
Fail to mention that this changes the behavior of how the screen resize works entirely, and disallow users to scroll beyond the initially clicked TextInput
, so if u have a list with multiple text fields, this solution isn't gonna work for you (It didn't for me and my use case).
The solution I'm suggesting is similar to stateAlwaysHidden|adjustPan
expect it uses android:windowSoftInputMode="adjustNothing"
instead. In essence, what this will do, is tell android to do nothing when the windowSoftInput
(i.e: the software keyboard) opens up. essentially eliminating the automatic layout adjustments. Which achieves two important things:
1.
, both platforms should now behave the same when the keyboard opens up.The solution itself, will be to listen to keyboard height changes (open/close) events, and to adjust a padding to the wrapping View
container, so it essentially "dodges" the keyboard, leaving the scrollview/listView/flatlist/etc (if present) above the keyboard, making it look seamless. This however, requires that we know the size of the view that needs to be padded, so we can extract the diff that needs to be padded (in case the View in question isn't the last element on the screen; incase you have bottom action buttons for example).
All of this can then be packed in a newly composed View
, we will called it KeyboardPaddedView
(not to confuse with the built-in KeyboardAvoidingView
, which I find to be garbage btw). This view will contain all the logic, and can then just be used instead of the original View, which wraps the Flatlist/ScrollView etc.
So again, summed up:
android:windowSoftInputMode="adjustNothing"
<!-- 1. Set `android:windowSoftInputMode="adjustNothing"` -->
<!-- AndroidManifest.xml -->
...
<activity
...
android:windowSoftInputMode="adjustNothing"
...
>
...
</activity>
// keyboard.hks.ts
// 2. Listen to keyboard hight changes (written in typescript)
// Keyboard event hook
export const useOnKeyboardEvent = (
eventName: KeyboardEventName,
onEvent: (event: KeyboardEvent) => void,
) => {
const cb = useCallback((event: KeyboardEvent) => onEvent(event), [onEvent]);
useEffect(() => {
const listener = Keyboard.addListener(eventName, cb);
return () => listener.remove();
}, [eventName, cb]);
};
type OnUpdateCB = (newHeight: number) => void;
// hook to calculate and set keyboard height
export const useOnKeyboardHeightUpdate = (onUpdate: OnUpdateCB) => {
const keyboardOpenEvent = useMemo(
() => (Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow'),
[],
);
const keyboardCloseEvent = useMemo(
() => (Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide'),
[],
);
const cb = useCallback((height: number) => onUpdate(height), [onUpdate]);
useOnKeyboardEvent(keyboardOpenEvent, (event) =>
cb(event.endCoordinates.height),
);
useOnKeyboardEvent(keyboardCloseEvent, () => cb(0));
};
// measures.hks.ts
// 3. Measure the View and its location to determine the needed diff/padding from the keyboard.
// hook to Measure a view's layout inside the container its in,
// in relation to the window, i.e its global/absolute position on screen
export const useWindowMeasuredViewRef = (): [
MutableRefObject<View | undefined>,
LayoutRectangle,
(el: View) => void,
] => {
const viewRef = useRef<View>();
const [layoutRect, setLayoutRect] = useState({ x: 0, y: 0, width: 0, height: 0 });
const callbackSetter = useCallback(async (el: View) => {
async function nextFrameAsync() {
return new Promise<void>((resolve) =>
requestAnimationFrame(() => resolve()),
);
}
// set viewRef element
viewRef.current = el;
// view measurements will only be available on the next frame, so we simply await the next frame,
// so we can correctly measure the view and its layout
await nextFrameAsync();
viewRef.current?.measureInWindow(
(x: number, y: number, width: number, height: number) => {
setLayoutRect({
x,
y,
height,
width,
});
},
);
}, []);
return [viewRef, layoutRect, callbackSetter];
};
// Using the measured view
// get needed bottom padding for keyboard height to offset FlatList component using bottom padding
// helps to get the view above the keyboard correctly, regardless of any view beneath it
export function useKeyboardPaddedHeightForView(): [number, (el: View) => void] {
const [_viewRef, layout, viewRefSetter] = useWindowMeasuredViewRef();
const windowDimensions = useWindowDimensions();
const [keyboardHeight, _setKeyboardHeight] = useState(0);
const diff = useMemo(
() => windowDimensions.height - layout.height - Math.floor(layout.y),
[windowDimensions.height, layout],
);
const setKeyboardHeight = useCallback(
(height: number) => {
_setKeyboardHeight(height - diff);
},
[_setKeyboardHeight, diff],
);
// use the above implemented keyboard calculation
useOnKeyboardHeightUpdate(setKeyboardHeight);
return [keyboardHeight, viewRefSetter];
}
// KeyboardPaddedView.tsx
// 4. Wrap everything inside the Measured view for reusability
type Props = ViewProps & {};
function KeyboardPaddedView(props: Props): ReactElement {
const { children, style, ...rest } = props;
const [keyboardHeight, viewRefSetter] = useKeyboardPaddedHeightForView();
const keyboardHeightStyle = {
paddingBottom: keyboardHeight > 0 ? keyboardHeight : 0,
};
return (
<View ref={viewRefSetter} style={[style, keyboardHeightStyle]} {...rest}>
{children}
</View>
);
}
export default KeyboardPaddedView;
That's it, the KeyboardPaddedView can now be used accordingly, to avoid the keyboard, on both iOS and Android, which will now have the same behavior.
Honestly at this point I don't think anyone will read this, but just in case anyone does, sorry for the long solution, if anyone actually ends up reading and/or using this, hope this helps.
Upvotes: 3
Reputation: 427
Replace the line in the AndroidManifest.xml file:
Before
android:windowSoftInputMode="adjustResize"
After
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
Upvotes: 0
Reputation: 99
Change the line in AndroidManifest.xml works for me, I did the update but that does not works
Change the config to:
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
Upvotes: 3
Reputation: 560
if you're using react native version "0.64.*". Just set react-native-screens version to "3.4.0". Do not use "^" to avoid npm updated it. and it worked for me.
Upvotes: 0
Reputation: 139
For those who have the same problem, having both botton and top padding often result on the keyboard closing itself when opening. Took a while to discover that
Upvotes: 0
Reputation: 1007
Change the line in AndroidManifest.xml
Old line
android:windowSoftInputMode="adjustResize"
New line
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
Upvotes: 18
Reputation: 11
Just Upgraded react-native-screen to latest version.
yarn add react-native-screens
"react-native-screens": "^3.13.1"
Upvotes: 1
Reputation: 355
Was facing a similar issue, turned out that the react-native-screens library was causing the problem. Try setting the version to "~3.10.2". Worked out for me.
Upvotes: 34