Adjust Android Keyboard - AndreiRadchenko/scenery GitHub Wiki
Adjust android keyboard on Login and Registration form.
Solution on expo issues | Stackowerflow discuss
I want to achieve form that move up on keyboard show. Keyboard obscures bottom part of form. Background image hasn't move.
I didn't use KeyboardAvoidingView, instead I did it by:
- prevent any keyboard impact on view. It's native behavior for iOs but not trivial for android.
- Next step was to create animation for form translate on keyboardWillShow (ios) and keyboardDidShow (android doesn't have keyboardWillShow)
First of all you need to understand three modes for softwareKeyboardLayoutMode: pan, resize in expo app.json (adjustPan, adjustResize, adjustNothing in android activity manifest .xml)
How to prevent android view resizing or moving on keyboard show in expo
Expo doesn't have adjustNothing mode. To achieve such behavior for android app I left default resize mode for softwareKeyboardLayoutMode in expo app.json and used trick in component composition: style={{ minHeight: Math.round(Dimensions.get('window').height) + StatusBar.currentHeight
You should probably add StatusBar.currentHeight if background image cover status bar as well.
Pay attention to style={{flex: 1}} in outer components.
import { Dimensions, StatusBar } from 'react-native';
export const LoginScreen = ({ navigation, route }) => {
const translateAnim = useFormAnimation({
formOffset: isPlatformIOS ? 250 : screenHeight * 0.36,
animationDuration: Platform.OS === 'ios' ? 200 : 200,
});
return (
<TouchableWithoutFeedback
onPress={() => {
Keyboard.dismiss();
}}
>
<Styled.Container>
<Styled.BgImage
resizeMode="cover"
source={require('../../../../assets/img/PhotoBG-compressed.jpg')}
style={{ minHeight: Math.round(Dimensions.get('window').height) + StatusBar.currentHeight}} // <-- This trick doesn't allow BgImage resize on keyboard show
>
<Styled.LoginForm
keyboardHeight={keyboardHeight}
isPlatformIOS={isPlatformIOS}
style={{
transform: [
{ scale: 1 },
{ rotateY: '0deg' },
{ perspective: 100 },
{ translateY: translateAnim },
],
}}
>
Form translate animation on keyboard show
useFormAnimation.js
import { Animated, Platform } from 'react-native';
import { useState, useEffect } from 'react';
import { useKeyboardVisible } from './useKeyboardVisible';
export const useFormAnimation = ({ formOffset, animationDuration }) => {
const keyboardHeight = useKeyboardVisible();
const [translateAnim] = useState(new Animated.Value(+0));
const [translateTo, setTranslateTo] = useState(+0);
useEffect(() => {
Animated.timing(translateAnim, {
toValue: translateTo,
duration: animationDuration,
useNativeDriver: true,
}).start();
}, [translateTo]);
useEffect(() => {
keyboardHeight
? setTranslateTo(
Platform.OS === 'ios'
? formOffset - keyboardHeight
: formOffset - keyboardHeight
)
: setTranslateTo(+0);
}, [keyboardHeight]);
return translateAnim;
};
useKeyboardVisible.js
import { useState, useEffect, useCallback } from 'react';
import { Keyboard, Platform } from 'react-native';
export const useKeyboardVisible = () => {
const [keyboardHeight, setKeyboardHeight] = useState(0);
useEffect(() => {
const keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
(event) => {
setKeyboardHeight(event.endCoordinates.height);
}
);
const keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
() => {
setKeyboardHeight(0);
}
);
return () => {
keyboardDidHideListener.remove();
keyboardDidShowListener.remove();
};
}, []);
return keyboardHeight;
};
Links for future research:
Create native modules to change adjustResize/adjustPan in screen | react-native-keyboard-controller
Consider that using custom library deprives you of testing app in Expo Go
View behavior with keyboard open on Comments screen.
With softwareKeyboardLayoutMode: 'resize' keyboard can decrease height of screen components if they have height set in percent or if its component is descendant of ScrollView. In first case root view height will be recalculated (initial screen height minus keyboard height) and component will occupy it percent part of recalculated root view. In second case ScrollView will shrink to make room for keyboard.
Screen comments.js
import { FlatList, KeyboardAvoidingView, Platform } from 'react-native';
import { CommentCard } from '../../../../components/CommentCard';
import { InputBottomBar } from '../../../../components/InputBottomBar';
import * as Styled from './Comments.styled';
import authors from '../../../../mock/authors.json';
const isPlatformIOS = Platform.OS === 'ios';
const ImageCard = ({ url }) => {
return <Styled.ImageCard source={{ uri: url }} />;
};
export const CommentsScreen = ({ navigation, route }) => {
const { image, comments } = route.params.post;
return (
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={isPlatformIOS ? 'padding' : null}
keyboardVerticalOffset={isPlatformIOS ? 65 : 0}
keyboardShouldPersistTaps="handled"
>
<Styled.CommentsContainer>
<FlatList
data={comments}
renderItem={({ item, index }) => {
const avatar = authors.find((e) => e._id === item.authorId);
const isLastComment = index === comments.length - 1;
return (
<CommentCard
{...item}
index={index}
avatar={avatar}
isLastComment={isLastComment}
/>
);
}}
ListHeaderComponent={<ImageCard url={image.url} />}
/>
<InputBottomBar />
</Styled.CommentsContainer>
</KeyboardAvoidingView>
);
};