kirillzyusko / react-native-keyboard-controller

Keyboard manager which works in identical way on both iOS and Android
https://kirillzyusko.github.io/react-native-keyboard-controller/
MIT License
1.54k stars 61 forks source link

Android Keyboard Height - 24px too small (most of the time) #509

Closed mattlennon3 closed 1 month ago

mattlennon3 commented 1 month ago

Describe the bug We recently updated to these versions:

react-native-keyboard-controller: 1.9.0 -> 1.12.6

react-native-reanimated: 3.8.1 -> 3.13.0

Since then, we noticed a space appear between the keyboard and our buttons at the bottom of our screens:

Screenshot 2024-07-17 at 16 26 01

I switched between versions and calculated this as exactly 24px difference. I believe it has something do with https://github.com/kirillzyusko/react-native-keyboard-controller/pull/468 and the issues it is trying to fix.

Code snippet I don't have a full repo for you. But I'm happy to share the component we insert at the bottom of any pages which need to avoid the keyboard.

Please ignore the iOS max height code. We also don't pass any extraHeight in for the problem screens. So it is 0.

Otherwise the code is quite close to the example in this repo I believe:

import { useState } from 'react';
import { Platform } from 'react-native';
import { useReanimatedKeyboardAnimation } from 'react-native-keyboard-controller';
import Animated, { runOnJS, useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

interface KeyboardBottomPaddingViewProps {
  extraHeight?: number;
}

export const KeyboardBottomPaddingView = ({ extraHeight = 0 }: KeyboardBottomPaddingViewProps) => {
  const { height: kbHeight } = useReanimatedKeyboardAnimation();

  console.log({ kbHeight, statusBarHeight: StatusBar.currentHeight });

  const insets = useSafeAreaInsets();
  const extra = (Platform.OS === 'ios' ? insets.bottom : 0) + extraHeight;

  /** Workaround for an ios keyboard bug, the password prompt jumps on each keydown. So we keep the max height here, unless it goes back to 0. */
  const [maxHeight, setMaxHeight] = useState(kbHeight.value);
  const handleHeightChange = (newHeight: number) => {
    if (newHeight === 0) {
      setMaxHeight(0);
    } else if (newHeight < maxHeight) {
      setMaxHeight(newHeight);
    }
  };
  useDerivedValue(() => {
    runOnJS(handleHeightChange)(kbHeight.value);
  });
  /** End workaround */

  const styles = useAnimatedStyle(() => {
    const height = -maxHeight - extra;
    return {
      height,
    };
  }, [extra, maxHeight]);

  if (!maxHeight) {
    // This will avoid the gap at the bottom when there is no keyboard in the view
    return null;
  }

  return <Animated.View style={styles} />;
};

The logs output from the console here are as follows:

// new version
{"kbHeight": -302.9090881347656, "statusBarHeight": 52.727272033691406}
// old version
{"kbHeight": -278.9090881347656, "statusBarHeight": 52.727272033691406}

It may also be important to note the provider props, which we wrap our entire app within:

<KeyboardProvider statusBarTranslucent navigationBarTranslucent>

I know this might be related to the StatusBar, so thought I'd include that.

Repo for reproducing

N/A, sorry! Can try and create a new repo if absolutely required.

To Reproduce

Expected behavior I expect the height value returned by useReanimatedKeyboardAnimation to match the height of the keyboard.

Screenshots (Above)

Smartphone (please complete the following information):

Tested on a Pixel 5 (screenshot). But I see this on a Pixel 7 and 2 also.

Additional context Just a thanks for maintaining this library :). If I can provide more info just ask!

kirillzyusko commented 1 month ago

Hey @mattlennon3

Maybe it's because of this PR https://github.com/kirillzyusko/react-native-keyboard-controller/commit/97d504d0a937a59a87f3e0fc63f669dbc3b402f5?

Prior to this fix all movement inside navigation bar was considered as a non keyboard movement (i. e. if keyboard was shown for let's say 10px and navigation bar translucent, then my hook returned 0).

Can you tell me how do you handle translucency of navigationBar in your code? Do you just set it to translucent and then you are adding a fixed padding to all screens?

If I understand the problem correctly, then you need to change your code to something like this:

const extra = insets.bottom + extraHeight; // no more Platforms check 😎 

Let me know if it helps or not, and if yes then I'll be happy to elaborate more on why it has been changed and why it works in such way now 👀

mattlennon3 commented 1 month ago

Hey @kirillzyusko, thanks so much for the fast reply!

I think removing that check has fixed it. I can remove some magic numbers that I was using as a workaround before I raised this issue too! 🙏

Hmm, I think we use this for navigationBar and statusBar config:

const statusBarDefaults = {
  navigationBarColor: '#ffffff00',
  statusBarColor: 'transparent',
  statusBarStyle: 'dark',
  statusBarTranslucent: true,
} as const;

Are any of these values working against me here?

I notice internally we do indeed have an extra bit of bottom padding on every screen that pushes the buttons up even further. But this is existing. I might need to reduce that on smaller devices, but that's a "me" problem :).

I see, so before, the keyboard height was actually not including the navbar height at the bottom, but now it is? So adding insets to android will give me the correct height?

Thanks again for your reply.

kirillzyusko commented 1 month ago

Are any of these values working against me here?

No, everything is okay here 🙂

I see, so before, the keyboard height was actually not including the navbar height at the bottom, but now it is?

Yeah, this is something that should be fixed in the first library version. Because without this fix you need to write a conditional code to handle bottom padding. But with this fix I unified the approach and now it works in the same way on both iOS and Android.

So adding insets to android will give me the correct height?

Yes.

Thanks again for your reply.

My pleasure 😊 Can I close the issue? 👀

mattlennon3 commented 1 month ago

Great, thanks so much. I'll close it down. Have a nice day! 😄