Skip to content

Keyboard height not updating on iOS with useKeyboardHandler (works on Android) #991

@mainkunalhu

Description

@mainkunalhu

Describe the bug
On iOS devices, the keyboard height is not updating correctly when using react-native-keyboard-controller with useKeyboardHandler. The same implementation works perfectly on Android, but fails to respond consistently or at all on iOS when trying to animate a view based on keyboard height.

Code snippet

import {
  Dimensions,
  Keyboard,
  Platform,
  Pressable,
  StyleSheet,
  Text,
  TextInput,
  View,
} from "react-native";
import React, {useEffect, useRef, useState} from "react";
import {useNavigation, useRoute} from "@react-navigation/native";
import {Color, FontFamily} from "../../Styles/Colors";
import {useSafeAreaInsets} from "react-native-safe-area-context";
import BackButton from "../../Assets/SVGs/BackButton";
import {
  responsiveFontSize,
  responsiveHeight,
} from "react-native-responsive-dimensions";
import {useFoldableDevice} from "../../Constant/foldPhone/FoldableDeviceProvider";
import {GetUserConversation} from "../../Services/chats";
import FastImage from "@d11/react-native-fast-image";
import Animated, {
  useAnimatedStyle,
  useSharedValue,
} from "react-native-reanimated";
import {
  useKeyboardHandler,
  useKeyboardAnimation,
} from "react-native-keyboard-controller";
import SendBtn from "../../Assets/SVGs/SendBtn";

const useGradualAnimation = () => {
  const height = useSharedValue(0);
  useKeyboardHandler(
    {
      onMove: e => {
        "worklet";
        height.set(e.height);
      },
      //   onEnd: e => {
      //     "worklet";
      //     height.set(e.height);
      //   },
    },
    [],
  );
  return {height};
};

const {width, height} = Dimensions.get("screen");
const ParticularChatsScreen = () => {
  const routes = useRoute();
  const {conversationId, othersUserId} = routes.params;
  const insets = useSafeAreaInsets();
  const navigation = useNavigation();
  const {isUnfolded} = useFoldableDevice();
  const flatListRef = useRef(null);
  //   const {height: keyboardHeight} = useKeyboardAnimation();
  //   const keyboardHeight = useSharedValue(0);

  const [data, setData] = useState([
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
    {
      _id: "685a43b8335b4f21bb7a716d",
      conversationId: "conv_RidBd7fH2QxR",
      senderId: "user_123",
      receiverId: "U15281858739",
      content: "Hello",
      messageType: "text",
      isRead: false,
      isDelivered: false,
      messageId: "msg_rXiyMEgtVAPT",
      timestamp: "2025-06-24T06:20:40.744Z",
      createdAt: "2025-06-24T06:20:40.745Z",
      updatedAt: "2025-06-24T06:20:40.745Z",
    },
  ]);

  const getChatsData = async () => {
    try {
      const {result} = await GetUserConversation(conversationId, 1, 20);
      console.log(result);
      //   setData(`${JSON.stringify(result?.data?.messages[0]?.content)}`);
    } catch (error) {
      console.error(error.message);
      //   setData(`${error.message}`);
    }
  };

  useEffect(() => {
    // const keyboardDidShowListener = Keyboard.addListener(
    //   "keyboardDidShow",
    //   e => {
    //     setHeightValue(e.endCoordinates.height);

    //     keyboardHeight.set(e.endCoordinates.height);
    //   },
    // );
    // const keyboardDidHideListener = Keyboard.addListener(
    //   "keyboardDidHide",
    //   () => {
    //     setHeightValue(0);

    //     keyboardHeight.set(0);
    //   },
    // );

    getChatsData();
    flatListRef.current.scrollToEnd({animated: false});

    // return () => {
    //   keyboardDidShowListener.remove();
    //   keyboardDidHideListener.remove();
    // };
  }, []);

  const handleBackButton = () => {
    navigation.goBack();
  };

  const onContentSizeChange = () => {
    flatListRef.current.scrollToEnd({animated: false});
  };

  const ITEM_HEIGHT = responsiveHeight(10);

  const {height} = useGradualAnimation();
  const fakeViewStyles = useAnimatedStyle(() => {
    return {
      height: Math.abs(height),
    };
  }, []);

  return (
    <>
      <View style={[styles.topBar, {paddingTop: insets.top}]}>
        <Pressable
          onPress={handleBackButton}
          style={styles.backButtonContainer}>
          <BackButton
            color="black"
            width={
              Platform.OS === "ios"
                ? isUnfolded
                  ? responsiveFontSize(2.5)
                  : responsiveFontSize(4)
                : isUnfolded
                ? responsiveFontSize(2)
                : responsiveFontSize(4)
            }
            height={
              Platform.OS === "ios"
                ? isUnfolded
                  ? responsiveFontSize(3)
                  : responsiveFontSize(5)
                : isUnfolded
                ? responsiveFontSize(2)
                : responsiveFontSize(5)
            }
          />
        </Pressable>
        <Pressable
          onPress={() => {
            navigation.navigate("UserProfileScreen", {id: othersUserId});
          }}
          style={styles.profile}>
          <FastImage
            source={{
              uri: `https://rentlog-test.s3.ap-south-1.amazonaws.com/Profile_Pictures/${othersUserId}_profile_pic.jpeg`,
              priority: FastImage.priority.normal,
            }}
            resizeMode={FastImage.resizeMode.cover}
            style={styles.topImage}
          />
          <Text style={styles.name}>Shivam</Text>
        </Pressable>
      </View>
      <View style={{flex: 1, position: "relative"}}>
        {/*  chats */}
        <Animated.FlatList
          getItemLayout={(data, index) => ({
            length: ITEM_HEIGHT,
            offset: ITEM_HEIGHT * index,
            index,
          })}
          CellRendererComponent={({children}) => children}
          removeClippedSubviews={false}
          ref={flatListRef}
          bounces={false}
          horizontal={false}
          contentContainerStyle={{
            //   minHeight: height * 0.6,
            justifyContent: "flex-end",
            alignItems: "flex-end",
            paddingBottom:
              insets.bottom + responsiveFontSize(Platform.OS === "ios" ? 0 : 4),
            width: width,
            paddingHorizontal: responsiveFontSize(2),
          }}
          onContentSizeChange={onContentSizeChange}
          data={data}
          renderItem={({item, index}) => {
            return (
              <View key={item._id}>
                <Text>{item.content}</Text>
              </View>
            );
          }}
        />
        {/* input box */}
        <View
          style={{
            marginTop: responsiveFontSize(2),
            width: width * 0.92,
            height: responsiveHeight(7),
            justifyContent: "center",
            alignItems: "center",
            borderRadius: width,
            overflow: "hidden",
            padding: width * 0.003,
            marginHorizontal: "auto",
            marginBottom:
              insets.bottom + responsiveFontSize(Platform.OS === "ios" ? 0 : 4),
          }}>
          <View
            style={{
              width: "100%",
              height: "100%",
              borderRadius: width,
              borderWidth: width * 0.001,
              borderColor: "rgba(0, 0, 0, 0.46)",
              backgroundColor: Color.light_bg,
              elevation: 2,
              flexDirection: "row",
              overflow: "hidden",
              padding: width * 0.01,
              shadowColor: "#000",
              shadowOffset: {width: 0, height: 2},
              shadowOpacity: 0.2,
              shadowRadius: 1.41,
            }}>
            <TextInput
              placeholder="Message"
              style={{
                flex: 2,
                borderRadius: width,
                paddingHorizontal: responsiveFontSize(2),
                color: "rgba(0, 0, 0, 0.81)",
                height: "100%",
                fontFamily: FontFamily.GilroyBold,
                fontSize: responsiveFontSize(1.7),
              }}
              placeholderTextColor={"rgba(0, 0, 0, 0.49)"}
            />
            <Pressable
              style={{
                backgroundColor: Color.L1_red,
                justifyContent: "center",
                alignItems: "center",
                width: "13%",
                borderRadius: width,
                marginRight: width * 0.01,
              }}>
              <SendBtn
                height={responsiveFontSize(3)}
                width={responsiveFontSize(3)}
              />
            </Pressable>
          </View>
        </View>
        <Animated.View style={fakeViewStyles} />
      </View>
    </>
  );
};

export default ParticularChatsScreen;

const styles = StyleSheet.create({
  topBar: {
    flexDirection: "row",
    justifyContent: "flex-start",
    height: height * 0.1,
    alignItems: "center",
    paddingVertical: responsiveFontSize(1),
    gap: responsiveFontSize(1),
    width: width,
    borderBottomWidth: width * 0.001,
    borderColor: "rgba(0,0,0,0.3)",
  },
  backButtonContainer: {
    justifyContent: "center",
    alignItems: "center",
    width: width * 0.15,
    height: "100%",
  },
  profile: {
    flexDirection: "row",
    justifyContent: "center",
    alignItems: "center",
    gap: responsiveFontSize(2),
    marginBottom: width * 0.01,
  },
  topImage: {
    width: responsiveFontSize(4.5),
    borderRadius: width,
    height: responsiveFontSize(4.5),
  },
  name: {
    fontFamily: FontFamily.GilroyBold,
    fontSize: responsiveFontSize(2),
    color: Color.black,
  },
});

Repo for reproducing
sorry i cant provide the repo yet .

To Reproduce
Steps to reproduce the behavior:

  1. Clone the provided repository (or use the below code snippet).
  2. Open the app on iOS.
  3. Tap on a TextInput to bring up the keyboard.
  4. Observe that the animated view does not respond to keyboard height.
  5. Try the same on Android – it behaves as expected.

Expected behavior
The Animated.View should adjust its height according to the keyboardHeight on both Android and iOS, based on useSharedValue updates from the keyboard events.

Screenshots

issue.mov

Smartphone (please complete the following information):

  • Desktop OS: Mac OS 15.5
  • Device: Iphone 16 max pro emulator and
  • OS: Mac OS 15.5
  • RN version: 0.79.3
  • RN architecture: new
  • JS engine: JSC
  • Library version: 1.17.5

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