kirillzyusko / react-native-keyboard-controller

Keyboard manager which works in identical way on both iOS and Android
https://kirillzyusko.github.io/react-native-keyboard-controller/
MIT License
1.72k stars 74 forks source link

how can i change the fakeView height #292

Closed wangchongwei closed 11 months ago

wangchongwei commented 11 months ago

https://github.com/kirillzyusko/react-native-keyboard-controller/assets/25812026/076e7879-1dc4-4286-aeba-eba0297f7510

the bottom need input emoji icon.

how can i change the botton view?

thank you!

wangchongwei commented 11 months ago

now, it is not fluent.

kirillzyusko commented 11 months ago

@wangchongwei when you press "low"/"high" buttons you don't need to use values from useKeyboardAnimation hook. Can you provide a code example so that I can tell you more what needs to be changed?

wangchongwei commented 11 months ago
/**
 * test input
 */
import {useCallback, useEffect, useRef, useState} from 'react';
import {
  FlatList,
  ListRenderItemInfo,
  Pressable,
  StyleSheet,
  Text,
  TextInput,
  View,
  Keyboard,
} from 'react-native';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import {useReanimatedKeyboardAnimation} from 'react-native-keyboard-controller';
import Animated, {useAnimatedStyle} from 'react-native-reanimated';

const TEXT =
  'apple banana cat dog elephant fish goat horse igloo jacket kangaroo leaf moon noodles orange pineapple rabbit scarf telephone tree UFO volcano water x-ray yogurt zebra amazing beautiful clever delicious enormous fast funny gentle hungry intelligent jealousy kind lovely nervous noisy olympics polite quiet responsible sad talented ugly Vancouver Washington yesterday yourself';

type Item = {
  text: string;
  left: boolean;
};

const WORDS = TEXT.split(' ')
  .map(v => v.trim())
  .filter(v => v != null && v != '');

function genItems() {
  const items: Item[] = [];
  for (let i = 0; i < 20; i++) {
    let s = '';
    const count = Math.floor(Math.random() * 20 + 5);
    for (let j = 0; j < count; j++) {
      const index = Math.floor(Math.random() * WORDS.length);
      s = s + ' ' + WORDS[index];
    }
    items.push({
      text: s,
      left: Math.random() < 0.5,
    });
  }
  return items;
}

function Content() {
  const [items, setItems] = useState<Item[]>(genItems());
  const [inputText, setInputText] = useState('');
  const [showLowView, setShowLowView] = useState(false);
  const [showHightView, setShowHighView] = useState(false);

  const {height} = useReanimatedKeyboardAnimation();
  const [currentHeight, setCurrentHeight] = useState(0);
  const [currentHighHeight, setCurrentHighHeight] = useState(0);

  const refAnimatedView = useRef();

  const send = useCallback(
    (text: string) => {
      const item: Item = {
        text: text,
        left: Math.random() < 0.5,
      };
      const values = [item];
      values.push(...items);
      setItems(values);
    },
    [items],
  );

  const renderItem = useCallback((item: ListRenderItemInfo<Item>) => {
    return (
      <View style={styles.itemWrapper}>
        <View style={[styles.itemContainer, item.item.left ? styles.itemLeft : styles.itemRight]}>
          <View style={styles.avatar}>
            <Text style={styles.avatarText}>{item.index + 1}</Text>
          </View>
          <Text style={[styles.text, item.item.left ? styles.textRight : styles.textLeft]}>
            {item.item.text}
          </Text>
        </View>
      </View>
    );
  }, []);

  useEffect(() => {
    if (showLowView) {
      setCurrentHeight(40);
      setCurrentHighHeight(0);
    } else if (showHightView) {
      setCurrentHighHeight(400);
      setCurrentHeight(0);
    } else {
      setCurrentHeight(0);
      setCurrentHighHeight(0);
    }
  }, [height, showLowView, showHightView]);

  console.log('height ====>', height.value);
  const fakeView = useAnimatedStyle(
    () => ({
      height: Math.abs(height.value),
    }),
    [],
  );

  const showLow = () => {
    Keyboard.dismiss();
    setShowLowView(true);
    setShowHighView(false);
  };

  const showHigh = () => {
    Keyboard.dismiss();
    setShowLowView(false);
    setShowHighView(true);
  };

  return (
    <View style={{flex: 1, backgroundColor: 'white'}}>
      <FlatList data={items} renderItem={renderItem} style={styles.list} inverted />
      <View style={styles.inputRow}>
        <TextInput
          style={styles.input}
          multiline
          onChangeText={setInputText}
          value={inputText}
          autoFocus={false}
          onFocus={() => {
            setShowHighView(false);
            setShowLowView(false);
          }}
        />
        <Pressable
          style={styles.send}
          android_ripple={{color: '#DBDBDB88'}}
          onPress={() => {
            const text = inputText;
            if (text && text.length) {
              send(text);
              setInputText('');
            }
          }}>
          <Text style={styles.sendText}>SEND</Text>
        </Pressable>
        <Pressable
          style={styles.send}
          android_ripple={{color: '#DBDBDB88'}}
          onPress={() => {
            showLow();
          }}>
          <Text style={styles.sendText}>Low</Text>
        </Pressable>
        <Pressable
          style={styles.send}
          android_ripple={{color: '#DBDBDB88'}}
          onPress={() => {
            showHigh();
          }}>
          <Text style={styles.sendText}>High</Text>
        </Pressable>
      </View>
      <Animated.View ref={refAnimatedView} style={fakeView} />
      <Animated.View style={{backgroundColor: 'red', height: currentHeight}} />
      <Animated.View style={{backgroundColor: 'green', height: currentHighHeight}} />
    </View>
  );
}

export default function TestKeyboard() {
  return (
    <SafeAreaProvider>
      <Content />
    </SafeAreaProvider>
  );
}

const styles = StyleSheet.create({
  list: {
    flex: 1,
  },
  itemWrapper: {
    padding: 4,
  },
  itemContainer: {
    padding: 12,
    backgroundColor: 'lightgrey',
    borderRadius: 6,
  },
  itemLeft: {
    flexDirection: 'row',
  },
  itemRight: {
    flexDirection: 'row-reverse',
  },
  avatar: {
    width: 80,
    height: 80,
    backgroundColor: 'grey',
    borderRadius: 6,
    alignItems: 'center',
    justifyContent: 'center',
  },
  avatarText: {
    color: 'white',
    fontWeight: 'bold',
    fontSize: 36,
  },
  text: {
    fontSize: 18,
    color: 'black',
    textAlign: 'left',
    textAlignVertical: 'top',
    flex: 1,
  },
  textLeft: {
    marginRight: 6,
  },
  textRight: {
    marginLeft: 6,
  },
  inputRow: {
    margin: 4,
    gap: 4,
    alignItems: 'center',
    flexDirection: 'row',
  },
  send: {
    width: 70,
    height: 50,
    backgroundColor: '#2196f3',
    borderRadius: 6,
    alignItems: 'center',
    justifyContent: 'center',
  },
  sendText: {
    textAlign: 'center',
    textAlignVertical: 'center',
    color: 'white',
  },
  input: {
    flex: 1,
    backgroundColor: 'white',
    color: 'black',
    borderWidth: 1,
    borderColor: 'grey',
    borderRadius: 4,
  },
});
wangchongwei commented 11 months ago

thanks a lot.

kirillzyusko commented 11 months ago

@wangchongwei and what is the desired output? Right now code works exactly like you described? Can you share a screenshot of what should happen when you press low/high buttons?

wangchongwei commented 11 months ago

https://github.com/kirillzyusko/react-native-keyboard-controller/assets/25812026/30dfe809-443c-4d37-91e4-9ea6c4162f4c

wangchongwei commented 11 months ago

i hope like that. when i click the right button, bottom view will change. and i hope it is smoothly

kirillzyusko commented 11 months ago

Okay, I will look into the problem when I have a time:

But basically you'll need:

wangchongwei commented 11 months ago

Yes, thanks again.

kirillzyusko commented 11 months ago

@wangchongwei here is a modified variant of your code:

import {useCallback, useEffect, useRef, useState} from 'react';
import {
  FlatList,
  ListRenderItemInfo,
  Pressable,
  StyleSheet,
  Text,
  TextInput,
  View,
  Keyboard,
} from 'react-native';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import {KeyboardProvider, useReanimatedKeyboardAnimation} from 'react-native-keyboard-controller';
import Animated, {runOnJS, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming} from 'react-native-reanimated';

const TEXT =
  'apple banana cat dog elephant fish goat horse igloo jacket kangaroo leaf moon noodles orange pineapple rabbit scarf telephone tree UFO volcano water x-ray yogurt zebra amazing beautiful clever delicious enormous fast funny gentle hungry intelligent jealousy kind lovely nervous noisy olympics polite quiet responsible sad talented ugly Vancouver Washington yesterday yourself';

type Item = {
  text: string;
  left: boolean;
};

const WORDS = TEXT.split(' ')
  .map(v => v.trim())
  .filter(v => v != null && v != '');

function genItems() {
  const items: Item[] = [];
  for (let i = 0; i < 20; i++) {
    let s = '';
    const count = Math.floor(Math.random() * 20 + 5);
    for (let j = 0; j < count; j++) {
      const index = Math.floor(Math.random() * WORDS.length);
      s = s + ' ' + WORDS[index];
    }
    items.push({
      text: s,
      left: Math.random() < 0.5,
    });
  }
  return items;
}

function Content() {
  const [items, setItems] = useState<Item[]>(genItems());
  const [inputText, setInputText] = useState('');
  const [showLowView, setShowLowView] = useState(false);
  const [showHightView, setShowHighView] = useState(false);
  const prevKeyboardHeight = useRef(0);

  const keyboard = useReanimatedKeyboardAnimation();
  const ownKeyboardHeight = useSharedValue(0);
  const [currentHeight, setCurrentHeight] = useState(0);
  const [currentHighHeight, setCurrentHighHeight] = useState(0);
  const [showOwnKeyboard, setShowOwnKeyboard] = useState(false);

  const height = useDerivedValue(() => showOwnKeyboard ? ownKeyboardHeight.value : keyboard.height.value);

  const refAnimatedView = useRef();

  const send = useCallback(
    (text: string) => {
      const item: Item = {
        text: text,
        left: Math.random() < 0.5,
      };
      const values = [item];
      values.push(...items);
      setItems(values);
    },
    [items],
  );

  const renderItem = useCallback((item: ListRenderItemInfo<Item>) => {
    return (
      <View style={styles.itemWrapper}>
        <View style={[styles.itemContainer, item.item.left ? styles.itemLeft : styles.itemRight]}>
          <View style={styles.avatar}>
            <Text style={styles.avatarText}>{item.index + 1}</Text>
          </View>
          <Text style={[styles.text, item.item.left ? styles.textRight : styles.textLeft]}>
            {item.item.text}
          </Text>
        </View>
      </View>
    );
  }, []);

  useEffect(() => {
    if (showLowView) {
      Keyboard.dismiss();
      const currentKeyboardHeight = keyboard.height.value;
      // save current keyboard size as a previous one - later will be re-used on back transitions
      // our keyboard -> system keyboard
      prevKeyboardHeight.current = currentKeyboardHeight;
      // fit our keyboard view to match system keyboard layout
      ownKeyboardHeight.value = currentKeyboardHeight;
      // start smooth transition
      ownKeyboardHeight.value = withTiming(currentKeyboardHeight - 40, { duration: 250 });
      setShowOwnKeyboard(true);
      // setCurrentHeight(40);
      // setCurrentHighHeight(0);
    } else if (showHightView) {
      setCurrentHighHeight(400);
      setCurrentHeight(0);
    } else {
      ownKeyboardHeight.value = withTiming(prevKeyboardHeight.current, { duration: 250 }, (finished) => {
        // when layout is identical again - start to consume system keyboard layout metrics
        runOnJS(setShowOwnKeyboard)(false);
      });
      setCurrentHeight(0);
      setCurrentHighHeight(0);
    }
  }, [height, showLowView, showHightView]);

  console.log('height ====>', height.value);
  const fakeView = useAnimatedStyle(
    () => ({
      height: Math.abs(height.value),
    }),
    [],
  );

  const showLow = () => {
    setShowLowView(true);
    setShowHighView(false);
  };

  const showHigh = () => {
    Keyboard.dismiss();
    setShowLowView(false);
    setShowHighView(true);
  };

  return (
    <View style={{flex: 1, backgroundColor: 'white'}}>
      <FlatList data={items} renderItem={renderItem} style={styles.list} inverted />
      <View style={styles.inputRow}>
        <TextInput
          style={styles.input}
          multiline
          onChangeText={setInputText}
          value={inputText}
          autoFocus={false}
          onFocus={() => {
            setShowHighView(false);
            setShowLowView(false);
          }}
        />
        <Pressable
          style={styles.send}
          android_ripple={{color: '#DBDBDB88'}}
          onPress={() => {
            const text = inputText;
            if (text && text.length) {
              send(text);
              setInputText('');
            }
          }}>
          <Text style={styles.sendText}>SEND</Text>
        </Pressable>
        <Pressable
          style={styles.send}
          android_ripple={{color: '#DBDBDB88'}}
          onPress={() => {
            showLow();
          }}>
          <Text style={styles.sendText}>Low</Text>
        </Pressable>
        <Pressable
          style={styles.send}
          android_ripple={{color: '#DBDBDB88'}}
          onPress={() => {
            showHigh();
          }}>
          <Text style={styles.sendText}>High</Text>
        </Pressable>
      </View>
      <Animated.View ref={refAnimatedView} style={fakeView} />
      <Animated.View style={{backgroundColor: 'red', height: currentHeight}} />
      <Animated.View style={{backgroundColor: 'green', height: currentHighHeight}} />
    </View>
  );
}

export default function TestKeyboard() {
  return (
    <KeyboardProvider>
      <SafeAreaProvider>
        <Content />
      </SafeAreaProvider>
    </KeyboardProvider>
  );
}

const styles = StyleSheet.create({
  list: {
    flex: 1,
  },
  itemWrapper: {
    padding: 4,
  },
  itemContainer: {
    padding: 12,
    backgroundColor: 'lightgrey',
    borderRadius: 6,
  },
  itemLeft: {
    flexDirection: 'row',
  },
  itemRight: {
    flexDirection: 'row-reverse',
  },
  avatar: {
    width: 80,
    height: 80,
    backgroundColor: 'grey',
    borderRadius: 6,
    alignItems: 'center',
    justifyContent: 'center',
  },
  avatarText: {
    color: 'white',
    fontWeight: 'bold',
    fontSize: 36,
  },
  text: {
    fontSize: 18,
    color: 'black',
    textAlign: 'left',
    textAlignVertical: 'top',
    flex: 1,
  },
  textLeft: {
    marginRight: 6,
  },
  textRight: {
    marginLeft: 6,
  },
  inputRow: {
    margin: 4,
    gap: 4,
    alignItems: 'center',
    flexDirection: 'row',
  },
  send: {
    width: 70,
    height: 50,
    backgroundColor: '#2196f3',
    borderRadius: 6,
    alignItems: 'center',
    justifyContent: 'center',
  },
  sendText: {
    textAlign: 'center',
    textAlignVertical: 'center',
    color: 'white',
  },
  input: {
    flex: 1,
    backgroundColor: 'white',
    color: 'black',
    borderWidth: 1,
    borderColor: 'grey',
    borderRadius: 4,
  },
});

Here is a demo how it works:

https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/efdea1fb-a254-4299-ac5a-1c8ec2aff28a

I modified only keyboard -> low transition. I hope you can get an idea of how it should be done and can add other transitions on your own 😎

kirillzyusko commented 11 months ago

I'm going to close the issue (I hope I answered the question). Feel free to open a new one if it's needed 🙌