Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to handle Keyboard when footer contains TextInput #76

Open
spicy-xili opened this issue Aug 1, 2024 · 2 comments
Open

How to handle Keyboard when footer contains TextInput #76

spicy-xili opened this issue Aug 1, 2024 · 2 comments

Comments

@spicy-xili
Copy link

spicy-xili commented Aug 1, 2024

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.

textinput.mp4

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",
  },
});
@spicy-xili
Copy link
Author

spicy-xili commented Aug 1, 2024

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.

video.mp4

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

@ponikar
Copy link

ponikar commented Dec 10, 2024

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>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants