Creating a well-optimized sign-in screen in React Native often involves handling keyboard interactions to avoid input fields being hidden by the keyboard. In this guide, we’ll walk through building a keyboard-aware sign-in screen with animated adjustments, leveraging a custom hook to manage keyboard offset height. We’ll also add a header image and organize the screen for an aesthetically pleasing and functional layout.
Features of this Implementation:
- Keyboard Awareness: The screen adjusts its position based on the keyboard height.
- Smooth Animation: Animated transitions when the keyboard appears or disappears.
-
Reusable Custom Hook: A
useKeyboardOffsetHeight
hook to manage keyboard height dynamically.
1. Custom Hook: useKeyboardOffsetHeight
The custom hook useKeyboardOffsetHeight
listens for keyboard show/hide events and returns the keyboard height, which is crucial for animating the layout adjustments. This hook also ensures the functionality works across both iOS and Android.
import { useEffect, useState } from 'react';
import { Keyboard } from 'react-native';
export default function useKeyboardOffsetHeight() {
const [keyboardOffsetHeight, setKeyboardOffsetHeight] = useState(0);
useEffect(() => {
const showListener = Keyboard.addListener('keyboardWillShow', (e) => {
setKeyboardOffsetHeight(e.endCoordinates.height);
});
const hideListener = Keyboard.addListener('keyboardWillHide', () => {
setKeyboardOffsetHeight(0);
});
const androidShowListener = Keyboard.addListener('keyboardDidShow', (e) => {
setKeyboardOffsetHeight(e.endCoordinates.height);
});
const androidHideListener = Keyboard.addListener('keyboardDidHide', () => {
setKeyboardOffsetHeight(0);
});
return () => {
showListener.remove();
hideListener.remove();
androidShowListener.remove();
androidHideListener.remove();
};
}, []);
return keyboardOffsetHeight;
}
2. Animated Scale Button:
ScalePress.js
import { View, Text, Animated, TouchableOpacity } from 'react-native';
import React from 'react';
const ScalePress = ({ onLongPress, onPress, children, style }) => {
const scaleValue = new Animated.Value(1);
const onPressIn = () => {
Animated.spring(scaleValue, {
toValue: 0.96,
useNativeDriver: true,
}).start();
};
const onPressOut = () => {
Animated.timing(scaleValue, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
};
return (
<TouchableOpacity
onPress={onPress}
onPressOut={onPressOut}
onLongPress={onLongPress}
onPressIn={onPressIn}
activeOpacity={1}
style={style}
>
<Animated.View style={{ transform: [{ scale: scaleValue }], width: '100%' }}>
{children}
</Animated.View>
</TouchableOpacity>
);
};
export default ScalePress;
3. Main Component: App
The main component uses the custom useKeyboardOffsetHeight
hook and the Animated
API to manage smooth transitions for the sign-in form. The form includes email and password fields, a sign-in button, and a header image.
import React, { useEffect, useRef, useState } from 'react';
import { Animated, Image, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
import useKeyboardOffsetHeight from './useKeyboardOffsetHeight';
const App = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const keyboardOffsetHeight = useKeyboardOffsetHeight();
const animatedValue = useRef(new Animated.Value(0)).current;
const handleSignIn = () => {
// Handle sign-in logic here
console.log('Email:', email);
console.log('Password:', password);
};
// Animate view based on keyboard height
useEffect(() => {
Animated.timing(animatedValue, {
toValue: keyboardOffsetHeight ? -keyboardOffsetHeight * 0.5 : 0, // adjust "0.5" as per requirement to adjust scroll position
duration: 500,
useNativeDriver: true,
}).start();
}, [keyboardOffsetHeight]);
return (
<View style={styles.container}>
<View style={{ flex: 1 }}>
<Image
source={{
uri: 'https://cdn.shopaccino.com/igmguru/articles/Become-React-Native-Developer.png?v=496',
}}
style={styles.image}
resizeMode="cover"
/>
</View>
<Animated.ScrollView
bounces={false}
keyboardShouldPersistTaps="handled"
keyboardDismissMode="on-drag"
style={{ transform: [{ translateY: animatedValue }] }}
contentContainerStyle={styles.box}
>
<Text style={styles.title}>Sign In</Text>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
</Animated.ScrollView>
<ScalePress style={styles.signInButton} onPress={handleSignIn}>
<Text style={styles.buttonText}>Sign In</Text>
</ScalePress>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f9f9f9',
},
image: {
flex: 1,
borderRadius: 10,
},
box: {
flex: 1,
width: '100%',
backgroundColor: 'lightblue',
padding: 20,
borderRadius: 10,
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 20,
},
input: {
height: 50,
borderColor: '#ddd',
borderWidth: 1,
borderRadius: 8,
paddingHorizontal: 10,
marginBottom: 15,
fontSize: 16,
backgroundColor: '#f9f9f9',
},
signInButton: {
width: '100%',
marginTop: 20,
backgroundColor: '#4a90e2',
borderRadius: 8,
paddingVertical: 15,
alignItems: 'center',
marginBottom: 40,
},
buttonText: {
color: '#fff',
fontSize: 18,
fontWeight: 'bold',
},
});
export default App;
Summary
This keyboard-aware sign-in screen provides a smooth, user-friendly experience by:
- Using a custom hook to manage the keyboard offset height dynamically.
- Applying animations to keep the form visible when the keyboard is active.
- Structuring a visually appealing layout with an image header, well-styled input fields, and a prominent sign-in button.
By using this approach, you create a polished and functional text input UI, especially on mobile devices where screen space and user interactions with the keyboard are critical considerations. This setup can be expanded with more form fields or features, providing a great foundation for any React Native authentication flow.
Top comments (0)