2

In my App there are multiple situations where dialogs are shown. When a user should enter a Mail address and the format is wrong a react-native-paper Dialog will be opened inside a Portal.

<Portal>
  <KeyboardAvoidingView behavior={'padding'} style={{flex: 1}}>
     <SafeAreaView style={{flex: 1}}>
        <Dialog>
         [...]

When the Dialog appeared and afterwards the keyboard opens, everything moves up and it works fine! When the keyboard already is open and the Dialog appears, the dialog opens up behind the keyboard. Any Ideas how to fix?

I uploaded a video to show the behavior: https://streamable.com/zwin42

Already tried position and height as behavior as well, but it didnt work as well.

This only happens on ios! Android works fine!

1 Answers1

0

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>
   );
}