Skip to content

It didn't scroll to the right place and the footComponent disappears when keyboard is open using with @gorhom/bottom-sheet #1280

@hallee9000

Description

@hallee9000

Describe the bug
It didn't scroll to the right place and the footComponent disappears when keyboard is open

Code snippet

// test.tsx
import BottomSheetKeyboardAwareScrollView from "@/components/BottomSheetKeyboardAwareScrollView";
import Button from "@/components/common/Button";
import SubPageNavbar from "@/components/navbars/SubPage";
import { ThemedText } from "@/components/ThemedText";
import { Colors } from "@/constants/Colors";
import { BottomSheetFooter, BottomSheetModal } from "@gorhom/bottom-sheet";
import { router } from "expo-router";
import { useCallback, useEffect, useRef, useState } from "react";
import { Dimensions, Keyboard, SafeAreaView, StyleSheet, TextInput, View } from "react-native";
import Animated from "react-native-reanimated";
import { useSafeAreaInsets } from "react-native-safe-area-context";

const BottomSheetActions = ({ onCancel, onSave }: { onCancel: () => void, onSave: () => void }) => {
  const { bottom: bottomSafeArea } = useSafeAreaInsets();
  const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);

  useEffect(() => {
    const showSubscription = Keyboard.addListener('keyboardWillShow', () => {
      setIsKeyboardVisible(true);
    });
    const hideSubscription = Keyboard.addListener('keyboardDidHide', () => {
      setIsKeyboardVisible(false);
    });

    return () => {
      showSubscription.remove();
      hideSubscription.remove();
    };
  }, []);

  return (
    <View style={[styles.actionsContainer, { paddingBottom: isKeyboardVisible ? 16 : bottomSafeArea }]}>
      <Button title="Cancel" variant="secondary" onPress={onCancel} style={{ flex: 1 }} />
      <Button title="Save" onPress={onSave} style={{ flex: 1 }} />
    </View>
  );
};

function Test() {
  const ref = useRef<BottomSheetModal>(null);
  const insets = useSafeAreaInsets();
  const screenHeight = Dimensions.get('window').height - insets.top;

  const renderFooterComponent = useCallback(({ animatedFooterPosition }: { animatedFooterPosition: Animated.SharedValue<number> }) => {
    return (
      <BottomSheetFooter
        animatedFooterPosition={animatedFooterPosition}
      >
        <BottomSheetActions
          onCancel={() => ref.current?.dismiss()}
          onSave={() => ref.current?.dismiss()}
        />
      </BottomSheetFooter>
    );
  }, []);

  return (
    <SafeAreaView style={styles.container}>
      <SubPageNavbar title="Test" onBack={() => router.back()} />
      <View style={styles.innerContainer}>
        <Button title="Test" onPress={() => ref.current?.present()} />
      </View>
      <BottomSheetModal
        ref={ref}
        enableDynamicSizing={true}
        maxDynamicContentSize={screenHeight}
        enablePanDownToClose={true}
        footerComponent={renderFooterComponent}
      >
        <BottomSheetKeyboardAwareScrollView
          style={styles.scrollContainer}
          contentContainerStyle={[
            styles.contentContainer,
            { paddingBottom: insets.bottom + 64 },
          ]}
        >
          <View style={styles.contentItem}></View>
          <ThemedText type="title2" isEmphasized>Test</ThemedText>
          <TextInput placeholder="Test" style={styles.input} />
        </BottomSheetKeyboardAwareScrollView>
      </BottomSheetModal>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: Colors.light.surfaceBase,
  },
  innerContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  scrollContainer: {
    backgroundColor: Colors.light.surfaceBase,
  },
  contentContainer: {
    padding: 20,
  },
  contentItem: {
    height: 400,
    backgroundColor: Colors.light.surfaceSubtle,
  },
  input: {
    borderRadius: 12,
    padding: 16,
    backgroundColor: Colors.light.fillRaised,
  },
  actionsContainer: {
    flexDirection: 'row',
    gap: 12,
    paddingHorizontal: 20,
    paddingTop: 8,
    backgroundColor: Colors.light.surfaceBase,
  },
});

export default Test;
// BottomSheetKeyboardAwareScrollView.tsx
import {
  SCROLLABLE_TYPE,
  createBottomSheetScrollableComponent,
  type BottomSheetScrollViewMethods,
} from "@gorhom/bottom-sheet";
import type { BottomSheetScrollViewProps } from "@gorhom/bottom-sheet/src/components/bottomSheetScrollable/types";
import { memo } from "react";
import {
  KeyboardAwareScrollView,
  KeyboardAwareScrollViewProps,
} from "react-native-keyboard-controller";
import Reanimated from "react-native-reanimated";

const AnimatedScrollView =
  Reanimated.createAnimatedComponent<KeyboardAwareScrollViewProps>(
    KeyboardAwareScrollView,
  );
const BottomSheetScrollViewComponent = createBottomSheetScrollableComponent<
  BottomSheetScrollViewMethods,
  BottomSheetScrollViewProps
>(SCROLLABLE_TYPE.SCROLLVIEW, AnimatedScrollView);
const BottomSheetKeyboardAwareScrollView = memo(BottomSheetScrollViewComponent);

BottomSheetKeyboardAwareScrollView.displayName =
  "BottomSheetKeyboardAwareScrollView";

export default BottomSheetKeyboardAwareScrollView as (
  props: BottomSheetScrollViewProps & KeyboardAwareScrollViewProps,
) => ReturnType<typeof BottomSheetKeyboardAwareScrollView>;

Repo for reproducing
None

Screenshots

Screen.Recording.2026-01-17.at.15.33.33.mov

Smartphone (please complete the following information):

  • Desktop OS: MacOS 15.7.3
  • Device: iPhone16 Simulator
  • OS: iOS 18.1
  • RN version: 0.79.5
  • Expo version: 53.0.22
  • @gorhom/bottom-sheet: 5.2.8

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions