Bryan
Bryan

Reputation: 633

When keyboard is already open and render() updates to show a KeyboardAvoidingView (that it wasn't showing before) it does not avoid the keyboard

So I have a restaurant listing with a filter at the top and below that the restaurants are updated as you type to match your filter. When your filter does not return anything the view is then changed to show a "No restaurants found, change your filter" type message. I have this set up as a KeyboardAvoidingView because I wanted it centered vertically, and when you have the keyboard open it's sitting just barely above it, and doesn't look great. With the view active however it's pushed up a bit and becomes centered with what is visible.

My problem is that once the filter is used enough to hide all restaurants, the KAV is then shown in the render function but doesn't recognize that the keyboard is open and it needs to move. If I get to that stage and then close/re-open the keyboard it works, but that is far from ideal.

I've already checked out this thread and tried the solution found there: KeyboardAvoidingView - Reset height when Keyboard is hidden but that did not work for me unfortunately.

And this thread seems to have the same problem as I do: react native KeyboardAvoidingView with already opened keyboard dont work properly but the solution was to hide the keyboard, which I do not want. I want them to correct their input and reduce the filter immediately using the already-open keyboard.

Is there a way I can introduce the KAV after the keyboard is already open and have it react to the already-opened keyboard?

Upvotes: 1

Views: 782

Answers (1)

Artem Diashkin
Artem Diashkin

Reputation: 11

UPDATE: Try to use this lib instead: https://github.com/kirillzyusko/react-native-keyboard-controller It solved a lot of our problems.


This is what worked for us

  1. We need to create a hook that will tell us that keyboard will hide (we will need it later):
import { useEffect, useState } from 'react';
import { Keyboard } from 'react-native';

export const useKeyboardVisible = () => {
    const [isKeyboardWillHide, setKeyboardWillHide] = useState(false);

    useEffect(() => {
        const keyboardWillHideListener = Keyboard.addListener('keyboardWillHide', () => {
            setKeyboardWillHide(true);
        });

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

    return { isKeyboardWillHide };
};
  1. Next we will need to create a hook that will return us animated marginBottom styles (for smooth layout change):
  • establish that keyboard is already visible using Keyboard.isVisible();;
  • get keyboard height using Keyboard.metrics();
  • if keyboard is visible -> set marginBottom = keyboard height
import { Keyboard } from 'react-native';
import { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';

const useKeyboardAlreadyVisibleMarginFix = () => {
    const { isKeyboardWillHide } = useKeyboardVisible();

    const marginBottom = useSharedValue(0);

    const animatedStyle = useAnimatedStyle(() => {
        return {
            marginBottom: marginBottom.value
                ? marginBottom.value
                : withTiming(0, {
                      duration: 300,
                  }),
        };
    }, []);

    useEffect(() => {
        const isVisible = Keyboard.isVisible();
        const metrics = Keyboard.metrics();

        if (isVisible) {
            marginBottom.value = metrics?.height ?? 0;
        }

        if (isKeyboardWillHide) {
            marginBottom.value = 0;
        }

        return () => {
            marginBottom.value = 0;
        };
    }, [isKeyboardWillHide, marginBottom]);

    return { animatedStyle };
};
  1. Use `Animated.View for making layout animation more user-friendly:
import { Keyboard, KeyboardAvoidingView, Platform } from 'react-native';
import { useHeaderHeight } from '@react-navigation/elements';
import Animated from 'react-native-reanimated';

// ...
 const height = useHeaderHeight();

 const { animatedStyle } = useKeyboardAlreadyVisibleMarginFix();

if (Platform.OS === 'ios') {
   return (
      <KeyboardAvoidingView behavior="padding" style={{flex: 1}} keyboardVerticalOffset={height}>
         <Animated.View style={[{ flex: 1 }, animatedStyle]}>{children}</Animated.View>
      </KeyboardAvoidingView>
   );
}

Upvotes: 1

Related Questions