[Bug]: Bottom sheet does not go down after closing the keyboard if you use BottomSheetScrollView
Version
v5
Reanimated Version
v3
Gesture Handler Version
v2
Platforms
iOS
What happened?
The issue still exists, everything is the same.
Problem: When using BottomSheetScrollView, if you open and close the keyboard, BottomSheetScrollView starts to occupy all the space where the keyboard was and does not return to its original position.
This only happens if the scroll content overflows when the keyboard is open (see the two videos below that demonstrate this).
Reproduction steps
https://github.com/user-attachments/assets/35e00184-67ca-4e99-b363-bcfca67281aa
https://github.com/user-attachments/assets/ec6e0720-eaaf-49f0-97ee-29661cfe2267
Reproduction sample
https://snack.expo.dev/@exzos28/f615a2?platform=ios
I have the exact same error, I will continue to be attentive if you find a solution.
@ChaparroAlberto I temporarily fixed this like this:
const isKeyboardVisible = useKeyboardState(state => state.isVisible); // or Keyboard.addListener('keyboardDidHide')
useEffect(() => {
if (Platform.OS !== βiosβ) {
return;
}
if (!isKeyboardVisible) {
bottomSheetRef.current?.snapToIndex(0);
}
}, [bottomSheetRef, isKeyboardVisible]);
The call happens with some delay, but it's better than nothing.
Okay, thanks a lot. Do you put that code in the component where you use it? Or where do you put that code snippet?
in the place where you have access to the modal ref
@ChaparroAlberto I temporarily fixed this like this:
const isKeyboardVisible = useKeyboardState(state => state.isVisible); // or Keyboard.addListener('keyboardDidHide') useEffect(() => { if (Platform.OS !== βiosβ) { return; } if (!isKeyboardVisible) { bottomSheetRef.current?.snapToIndex(0); } }, [bottomSheetRef, isKeyboardVisible]);The call happens with some delay, but it's better than nothing.
thank you so much, i solve this issue with keyboard listener useEffect(() => { Keyboard.addListener("keyboardDidHide", () => { bottomSheetRef.current?.snapToIndex(0); }); }, []);
Here is my workaround for the keyboard avoiding bugs. This is a replacement for BottomSheetTextInput.
The code is the same as the original except I manually set the keyboard state status: KEYBOARD_STATUS.SHOWN / status: KEYBOARD_STATUS.HIDDEN, this fixes the keyboard avoiding getting out of sync. I also added a waitUntil to fix a race condition where focusing the text input doesn't work while the bottom sheet is animating.
import { ANIMATION_STATUS, KEYBOARD_STATUS, useBottomSheetInternal } from '@gorhom/bottom-sheet';
import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef } from 'react';
import type { NativeSyntheticEvent, TextInputFocusEventData, TextInputProps } from 'react-native';
import { TextInput as RNTextInput, findNodeHandle } from 'react-native';
import { TextInput } from 'react-native-gesture-handler';
import { waitUntil } from '../utils/MiscHelpers';
export type BottomSheetTextInputProps = TextInputProps;
const BottomSheetTextInputComponent = forwardRef<TextInput | undefined, BottomSheetTextInputProps>(
({ onFocus, onBlur, ...rest }, providedRef) => {
//#region refs
const ref = useRef<TextInput>(null);
//#endregion
//#region hooks
const { animatedKeyboardState, textInputNodesRef, animatedAnimationState } =
useBottomSheetInternal();
//#endregion
//#region callbacks
const handleOnFocus = useCallback(
async (args: NativeSyntheticEvent<TextInputFocusEventData>) => {
const targetValue = args.nativeEvent.target;
if (onFocus) {
onFocus(args);
}
// Wait until the animation is not running to avoid race condition
await waitUntil(() => animatedAnimationState.get().status !== ANIMATION_STATUS.RUNNING, 10);
animatedKeyboardState.set((state) => ({
...state,
target: targetValue,
status: KEYBOARD_STATUS.SHOWN,
}));
},
[onFocus, animatedKeyboardState, animatedAnimationState]
);
const handleOnBlur = useCallback(
(args: NativeSyntheticEvent<TextInputFocusEventData>) => {
const keyboardState = animatedKeyboardState.get();
const currentFocusedInput = findNodeHandle(
RNTextInput.State.currentlyFocusedInput() as any
);
/**
* we need to make sure that we only remove the target
* if the target belong to the current component and
* if the currently focused input is not in the targets set.
*/
const shouldRemoveCurrentTarget = keyboardState.target === args.nativeEvent.target;
const shouldIgnoreBlurEvent =
currentFocusedInput && textInputNodesRef.current.has(currentFocusedInput);
if (shouldRemoveCurrentTarget && !shouldIgnoreBlurEvent) {
animatedKeyboardState.set((state) => ({
...state,
target: undefined,
status: KEYBOARD_STATUS.HIDDEN,
}));
}
if (onBlur) {
onBlur(args);
}
},
[onBlur, animatedKeyboardState, textInputNodesRef]
);
//#endregion
//#region effects
useEffect(() => {
const componentNode = findNodeHandle(ref.current);
if (!componentNode) {
return;
}
if (!textInputNodesRef.current.has(componentNode)) {
textInputNodesRef.current.add(componentNode);
}
return () => {
const componentNode = findNodeHandle(ref.current);
if (!componentNode) {
return;
}
const keyboardState = animatedKeyboardState.get();
/**
* remove the keyboard state target if it belong
* to the current component.
*/
if (keyboardState.target === componentNode) {
const currentState = animatedKeyboardState.get();
animatedKeyboardState.set({
...currentState,
target: undefined,
});
}
if (textInputNodesRef.current.has(componentNode)) {
textInputNodesRef.current.delete(componentNode);
}
};
}, [textInputNodesRef, animatedKeyboardState]);
useImperativeHandle(providedRef, () => ref.current ?? undefined, []);
//#endregion
return <TextInput ref={ref} onFocus={handleOnFocus} onBlur={handleOnBlur} {...rest} />;
}
);
export const BottomSheetTextInput = memo(BottomSheetTextInputComponent);
BottomSheetTextInput.displayName = 'BottomSheetTextInput';
+1, same issue
+1, similar issue but using BottomSheetModal with BottomSheetView.
I created a workaround that is working smoothly.
import { BottomSheetModal } from "@gorhom/bottom-sheet";
import { useEffect, useRef } from "react";
import { Keyboard } from "react-native";
const MyCustomSheetModal = () => {
//#region refs
const sheetRef = useRef<BottomSheetModal>(null);
const lastIndexRef = useRef(-1);
//#region effects
useEffect(() => {
const listener = Keyboard.addListener("keyboardDidHide", () => {
sheetRef.current?.snapToIndex(lastIndexRef.current);
});
return () => {
listener.remove();
}
}, []);
//#region handlers
function handleChange(index: number) {
if (!Keyboard.isVisible()) {
lastIndexRef.current = index;
}
}
return <BottomSheetModal ref={sheetRef} onChange={handleChange} />
}
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.
Same issue
+1
+1