Eduard
Eduard

Reputation: 9165

How to get a height of a Keyboard in React-Native?

I am using React-Navigation in my app and the app consists of StackNavigator with multiple screens, some screens of which have TextInput with autoFocus={true}

Problem: on these screens when the component renders, the height of the screen is being set in the constructor:

constructor(props) {
    super(props);
    this.state = { 
        height: Dimensions.get('window').height,
    };
}

But, since the autoFocus of TextInput is true, the keyboard on this screen pops-up on the screen almost instantly after the render, causing the component to re-render due to the eventListener that is added to Keyboard in componentWillMount:

 componentWillMount() {
    this.keyboardWillShowListener = Keyboard.addListener(
        "keyboardWillShow",
        this.keyboardWillShow.bind(this)
    );
}

keyboardWillShow(e) {
    this.setState({
        height:
            Dimensions.get("window").height * 0.9 - e.endCoordinates.height
    });
    LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
}

This affects the performance and I would like to avoid the unnecessary re-renders.

Questions:
1. Is it possible to set the dynamic height (depending on the device) of the Keyboard in React-Navigation's ScreenProps?
2. Is it possible to do the same with React-Navigation's state.params?
3. Is there any other way to solve this problem, apart from applying KeyboardAvoidingView or this module ?

Upvotes: 75

Views: 99005

Answers (8)

Muhammad Areeb
Muhammad Areeb

Reputation: 306

KeyboardListener = Keyboard.addListener("keyboardDidShow", (e) => {
    //you will get keyboard height in this
    const height =e.endCoordinates.height
    console.log("keyboard height ",height);
    //write your logic here
  })

Don't forget to Unsubscribe while leaving the Page

KeyboardListener.remove();

For more Info you can check the official Docs here For more https://reactnative.dev/docs/keyboard#addlistener

Upvotes: 0

John Franke
John Franke

Reputation: 1515

import { useEffect, useState } from "react";
import { Keyboard } from "react-native";

export const useKeyboardHeight = () => {
  const [keyboardHeight, setKeyboardHeight] = useState(undefined);
  useEffect(() => {
    Keyboard.addListener("keyboardDidShow", (e) => {
        const height = e.endCoordinates.height;
        setKeyboardHeight(height);
      
    });
  }, []);
  return keyboardHeight;
};

Here is a hook!

example of a view that sizes correctly with the keyboard open

const Container = () => {
    const height = useKeyboardHeight()
    return <View style={{ height, backgroundColor: "#ff00ff" }}/>
}

Upvotes: 0

Kevin Amiranoff
Kevin Amiranoff

Reputation: 14468

Edit 2024: If you have the option and use reanimated I would recommend using useAnimatedKeyboard as it performs a lot better in my opinion.

I also needed a hook for it, so that is how I get the height of the keyboard (largely inspired by the other answer), the code example is in TypeScript:

import { useEffect, useState } from 'react';
import { Keyboard, KeyboardEvent } from 'react-native';

export const useKeyboard = () => {
  const [keyboardHeight, setKeyboardHeight] = useState(0);

  useEffect(() => {
    function onKeyboardDidShow(e: KeyboardEvent) { // Remove type here if not using TypeScript
      setKeyboardHeight(e.endCoordinates.height);
    }

    function onKeyboardDidHide() {
      setKeyboardHeight(0);
    }

    const showSubscription = Keyboard.addListener('keyboardDidShow', onKeyboardDidShow);
    const hideSubscription = Keyboard.addListener('keyboardDidHide', onKeyboardDidHide);
    return () => {
      showSubscription.remove();
      hideSubscription.remove();
    };
  }, []);

  return keyboardHeight;
};

then in your component:

  const keyboardHeight = useKeyboard();
  console.log(keyboardHeight);

Upvotes: 114

Raj Shah
Raj Shah

Reputation: 738

const { height: screenHeight } = Dimensions.get('window')
const { width: screenWidth } = Dimensions.get('window')


 useEffect(() => {
    const keyboardDidShowListener = Keyboard.addListener(
      'keyboardDidShow',
      () => {
        if (Platform.OS === 'ios') {
          let bottomValue

          if (screenHeight === 667) {
            // iPhone 8
            console.log('iPhone 8')
            bottomValue = 10
          } else if (screenHeight === 812 && screenWidth === 375) {
            // iPhone 12
            console.log('iPhone 12')
            bottomValue = 10 // Adjust this value as needed
          } else if (screenHeight === 736) {
            console.log('iPhone 8 Plus')
            // iPhone 8 Plus
            bottomValue = 10 // Adjust this value as needed
          } else {
            console.log('iPhone')
            // Default value for other devices
            bottomValue = 10
          }

          setBottomValue(bottomValue)
        } else {
          setBottomValue(screenHeight === 667 ? 20 : 50)
        }
      }
    )

    const keyboardDidHideListener = Keyboard.addListener(
      'keyboardDidHide',
      () => {
        setBottomValue(0)
      }
    )

    return () => {
      keyboardDidShowListener.remove()
      keyboardDidHideListener.remove()
    }
  }, [screenHeight, screenWidth])

Upvotes: 0

Hassan Riaz
Hassan Riaz

Reputation: 106

You can create your own component and call it like this no third party package required.

import React, { useEffect, useLayoutEffect, useState } from "react";
import {
    Keyboard,
    KeyboardEvent,
    View
} from "react-native";
import { TComponent } from "./constraints";
import styles from "./styles";

export const KeyboardSpacerView: React.FC<TComponent> = (props) => {

    const [height, setHeight] = useState<number>(0);

    useEffect(()=>{
        // let event1 = Keyboard.addListener('keyboardWillShow', keyboardWillShow);
        let event2 = Keyboard.addListener('keyboardDidShow', keyboardWillShow);
        let event3 = Keyboard.addListener('keyboardWillHide', keyboardWillHide);
        
        return ()=>{
            // Keyboard.removeSubscription(event1);
            Keyboard.removeSubscription(event2);
            Keyboard.removeSubscription(event3);
        }
    }, [false])

    const keyboardWillShow = (event: KeyboardEvent) => {
        setHeight(event.endCoordinates.height);
    }

    const keyboardWillHide = (event: KeyboardEvent) => {
        setHeight(0);
    }

    return (
        <View style={[styles.container, {
            height: height
        }]} />
    );
}

and Use it like this

<View style={{flex: 1}}>
   <FlatList
      data={data}
      renderItem={Row}
   />
   <KeyboardSpacerView />
</View>

Upvotes: 2

Shawn
Shawn

Reputation: 521

For those of you still looking for an answer to this now you can use hooks.

import { useKeyboard } from '@react-native-community/hooks'

//Then keyboard like this 

const keyboard = useKeyboard()

console.log('keyboard isKeyboardShow: ', keyboard.keyboardShown)
console.log('keyboard keyboardHeight: ', keyboard.keyboardHeight)

Upvotes: 27

Ethan Naluz
Ethan Naluz

Reputation: 321

Just would like to add to the above answers, that using keyboardWillShow and keyboardWillHide rather than keyboardDidShow and keyboardDidHide will look much better. It just runs sooner and hence, looks smoother.

Upvotes: 10

Eduard
Eduard

Reputation: 9165

This is what I did:

If the app has "Authorization / Log-in / Sign-up screen" then:

  1. In componentWillMount add KeyboardListeners as explained here:

    this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
    this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
    
  2. Add autoFocus to e-mail / phone number / any other "first" TextInput on the page, so that when the screen loads, the Keyboard pops-up.

  3. In _keyboardDidShow function, that is used as a KeyboardListener, do the follows:

    _keyboardDidShow(e) {
        this.props.navigation.setParams({
            keyboardHeight: e.endCoordinates.height,
            normalHeight: Dimensions.get('window').height, 
            shortHeight: Dimensions.get('window').height - e.endCoordinates.height, 
        }); 
    }
    

    Dimensions is an API of React-Native, do not forget to import it just like you import any React-Native component.

  4. After that, while redirecting to the next page, pass these params and do not forget to keep on passing them to other screens in order not to lose this data:

    this.props.navigation.navigate('pageName', { params: this.props.navigation.state.params });
    

Upvotes: 94

Related Questions