react-native-true-sheet
react-native-true-sheet copied to clipboard
How to handle Keyboard when footer contains TextInput
I have put a TextInput in the footer component but I don't know how to properly handle the keyboard so that it doesn't hide the input itself. Normally I use KeyboardAvoidingView but it is not working at all. I also have tried keyboardMode="resize" but it didn't work. Any ideas? Thanks in advance! PS: I use Android.
https://github.com/user-attachments/assets/cae44391-dfdc-485e-9893-0304e2a4ed0f
This is my code:
import React, { forwardRef, useRef, useState, type Ref } from "react";
import { View, TextInput, TouchableOpacity, Text, StyleSheet, type ViewStyle } from "react-native";
import { TrueSheet, type TrueSheetProps } from "@lodev09/react-native-true-sheet";
import { FlatList } from "react-native-gesture-handler";
import { KeyboardAvoidingView } from "react-native-keyboard-controller";
import { DemoContent } from "./DemoContent";
const DARK = "#282e37";
const DARK_GRAY = "#333b48";
const SPACING = 16;
const INPUT_HEIGHT = SPACING * 3;
interface FlatListSheetProps extends TrueSheetProps {
postId: string;
userId: string;
}
const times = <T,>(length: number, iteratee: (index: number) => T): T[] => Array.from({ length }, (_, k) => iteratee(k));
export const CommentsSheet = forwardRef((props: FlatListSheetProps, ref: Ref<TrueSheet>) => {
const { postId, userId, ...rest } = props;
const flatListRef = useRef<FlatList>(null);
const [comment, setComment] = useState("");
const resetScroll = () => {
flatListRef.current?.scrollToOffset({ animated: false, offset: 0 });
};
const handleSend = () => {
console.log("Comment by user ${userId} on post ${postId}: ${comment}");
setComment(""); // Clear the input after sending
};
const resetTextInput = () => {
setComment(""); // Reset the text input
};
return (
<TrueSheet
ref={ref}
scrollRef={flatListRef}
sizes={["medium", "large"]}
cornerRadius={20}
dimmed={true}
blurTint="dark"
backgroundColor={DARK}
keyboardMode="pan"
grabber={false}
name={postId}
onDismiss={() => {
console.log("Sheet FlatList dismissed!");
resetScroll(); // Reset scroll position on dismiss
resetTextInput(); // Reset text input on dismiss
}}
onPresent={() => console.log("Sheet FlatList presented!")}
FooterComponent={
<View style={styles.footerContainer}>
<TextInput
style={styles.input}
placeholder="Write a comment..."
placeholderTextColor="#999"
value={comment}
onChangeText={setComment}
/>
<TouchableOpacity style={styles.sendButton} onPress={handleSend}>
<Text style={styles.sendButtonText}>Send</Text>
</TouchableOpacity>
</View>
}
{...rest}
>
<FlatList<number>
ref={flatListRef}
nestedScrollEnabled
data={times(50, (i) => i)}
contentContainerStyle={$content}
indicatorStyle="black"
renderItem={() => <DemoContent color={DARK_GRAY} />}
/>
</TrueSheet>
);
});
CommentsSheet.displayName = "CommentsSheet";
const $content: ViewStyle = {
padding: SPACING,
paddingTop: INPUT_HEIGHT + SPACING * 4,
};
const styles = StyleSheet.create({
footerContainer: {
flexDirection: "row",
alignItems: "center",
padding: SPACING,
backgroundColor: DARK,
},
input: {
flex: 1,
height: INPUT_HEIGHT,
backgroundColor: DARK_GRAY,
borderRadius: 20,
paddingHorizontal: SPACING,
color: "#fff",
},
sendButton: {
marginLeft: SPACING,
backgroundColor: "#1DA1F2",
borderRadius: 20,
paddingVertical: SPACING / 2,
paddingHorizontal: SPACING,
},
sendButtonText: {
color: "#fff",
fontWeight: "bold",
},
});
Update: I have moved the text input out of the footer. However, the keyboard animation looks weird. When you start typing, the screen below the sheet modal is visible for some ms in both keyboardMode types. Also, keyboardMode == pan partially hides the text input. If I place the TextInput to the top, as the one in the example, there are no visual bugs, but when it is in the bottom side of the screen, it clashes with the keyboard.
https://github.com/user-attachments/assets/3c700d33-aec1-4161-aa51-d4a8cb6f36fa
Code:
import React, { forwardRef, useRef, useState, type Ref } from "react";
import { View, TextInput, TouchableOpacity, StyleSheet, KeyboardAvoidingView, Platform } from "react-native";
import { TrueSheet, type TrueSheetProps } from "@lodev09/react-native-true-sheet";
import { FlatList } from "react-native-gesture-handler";
import { DemoContent } from "./DemoContent";
import { Ionicons } from "@expo/vector-icons";
const DARK = "#282e37";
const DARK_GRAY = "#333b48";
const COLORS = {
primary: "#282e37",
secondary: "#1DA1F2",
secondaryLighter: "#333b48",
secondaryEvenLighter: "#999",
primaryDarker: "#1c1f25",
};
const SPACING = 16;
const INPUT_HEIGHT = SPACING * 3;
interface FlatListSheetProps extends TrueSheetProps {
postId: string;
userId: string;
}
const times = <T,>(length: number, iteratee: (index: number) => T): T[] => Array.from({ length }, (_, k) => iteratee(k));
export const CommentsSheet = forwardRef((props: FlatListSheetProps, ref: Ref<TrueSheet>) => {
const { postId, userId, ...rest } = props;
const flatListRef = useRef<FlatList>(null);
const [comment, setComment] = useState("");
const resetScroll = () => {
flatListRef.current?.scrollToOffset({ animated: false, offset: 0 });
};
const handleSend = () => {
console.log(`Comment by user ${userId} on post ${postId}: ${comment}`);
setComment(""); // Clear the input after sending
};
const resetTextInput = () => {
setComment(""); // Reset the text input
};
return (
<TrueSheet
ref={ref}
scrollRef={flatListRef}
sizes={["medium", "large"]}
cornerRadius={20}
dimmed={true}
blurTint="dark"
keyboardMode="pan"
backgroundColor={DARK}
grabber={false}
name={postId}
onDismiss={() => {
console.log("Sheet FlatList dismissed!");
resetScroll(); // Reset scroll position on dismiss
resetTextInput(); // Reset text input on dismiss
}}
onPresent={() => console.log("Sheet FlatList presented!")}
{...rest}
>
<FlatList<number>
ref={flatListRef}
nestedScrollEnabled
data={times(10, (i) => i)}
contentContainerStyle={styles.flatListContentContainer}
indicatorStyle="black"
renderItem={() => <DemoContent color={DARK_GRAY} />}
/>
<View style={styles.cont}>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Write a comment..."
placeholderTextColor={COLORS.secondaryEvenLighter}
value={comment}
onChangeText={setComment}
/>
<TouchableOpacity style={styles.sendButton} onPress={handleSend}>
<Ionicons name="send" size={24} color={COLORS.secondary} />
</TouchableOpacity>
</View>
</View>
</TrueSheet>
);
});
CommentsSheet.displayName = "CommentsSheet";
const styles = StyleSheet.create({
flatListContentContainer: {
paddingHorizontal: 10,
paddingBottom: 70, // Add some padding to avoid overlapping with the input
},
cont: {
position: "absolute",
bottom: 0,
left: 0,
right: 0,
},
inputContainer: {
flexDirection: "row",
alignItems: "center",
padding: 10,
backgroundColor: COLORS.primary,
},
textInput: {
flex: 1,
padding: 10,
borderRadius: 20,
backgroundColor: COLORS.secondaryLighter,
color: COLORS.secondary,
fontSize: 14,
},
sendButton: {
marginLeft: 10,
backgroundColor: COLORS.primaryDarker,
borderRadius: 20,
padding: 10,
},
});
You can use useAnimatedKeyboard hook. This will give you realtime keyboard height in sync that can be used as paddingBottom to uplift your text input. This APIs are available in third party library react-native-reanimated.
For example
const keyboard = useAnimatedKeyboard();
const style = useAnimatedStyle(() => {
return {
paddingBottom: keyboard.height.value
}
})
return <Animated.View style={style}>
<TextInput />
</Animated.View>
Found any solution??