I have a <TextInput>
which, when the user enters anything into, will have a button appear from behind it.
This works fine using the Animated library from react, but each time I input text into the area, the animation "rubber bands" up and down.
My question is: How can I keep the button in place after the initial animation happens?
I still need to monitor whether the text box is empty in order to decide whether to show or hide the button.
My code:
import { View, TextInput, StyleSheet, Animated, TouchableOpacity } from "react-native";import { useState } from "react";import CurrencyInput from 'react-native-currency-input';import { Ionicons } from "@expo/vector-icons";type expenseItemData = { item:string; costAndSign:string; cost:number | null;}type Props = { sendItem: ({ item, cost, costAndSign }: expenseItemData) => void;}const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);const AddAndConfirmExpense: React.FC<Props> = ({sendItem}) => { const [animation] = useState(new Animated.Value(-58)); const [expenseValue, setExpenseValue] = useState<number>(0.00); const [expenseItem, setExpenseItem] = useState<string>(""); const [expenseValueAndSign, SetExpenseValueAndSign] = useState<string>(""); const [buttonLayoutPersistence, setButtonLayoutPersistence] = useState({ bottom:0 }); const [validExpenseItem, setValidExpenseItem] = useState<boolean>(false); const onChangeExpenseItem = (text: string) => { setExpenseItem(text); if (text.trim().length === 0) { setValidExpenseItem(false); hideButton(); setButtonLayoutPersistence({bottom:0}) return; } if (validExpenseItem) { setButtonLayoutPersistence({bottom:-48}) return }; showButton(); setValidExpenseItem(true); }; const onButtonPress = () => { const newData:expenseItemData = { item:expenseItem, costAndSign:expenseValueAndSign, cost:expenseValue } sendItem(newData); }; const setAreaDynamicStyling = () => { if (validExpenseItem) { return { borderTopRightRadius:5, borderTopLeftRadius:5, backgroundColor:"#f5f5f5" } } return {borderRadius:5}; }; const setButtonDynamicStyling = () => { if (!validExpenseItem) return {borderRadius:5} return {borderBottomLeftRadius: 5,borderBottomRightRadius:5} }; const animatedStyle = {transform: [{translateY:animation}],}; const showButton = () => { Animated.timing(animation, { toValue: -10, duration: 1000, useNativeDriver: true, }).start(); } const hideButton = () => { Animated.timing(animation, { toValue: -58, duration: 500, useNativeDriver: true, }).start(); } return (<View><View style={validExpenseItem ? [styles.inputsContainer, setAreaDynamicStyling()] : [styles.inputsContainer,styles.shadowProp,setAreaDynamicStyling()]}><TextInput style={styles.textInputArea} placeholder='Item' placeholderTextColor="#aaaaaa" onChangeText={(text) => {onChangeExpenseItem(text)}} value={expenseItem} underlineColorAndroid="transparent" autoCapitalize="none" /><View style={styles.verticalLine}/><CurrencyInput style={styles.currencyInputArea} value={expenseValue} onChangeValue={setExpenseValue} prefix="£" delimiter="," separator="." precision={2} minValue={0} onChangeText={(formattedValue) => { SetExpenseValueAndSign(formattedValue) }} /></View><AnimatedTouchable onPress={()=>{onButtonPress()}} style={[{flex:1, zIndex:-1},animatedStyle]}><View style={[styles.confirmInputContainer, setButtonDynamicStyling(), buttonLayoutPersistence]}> <Ionicons name="checkmark-circle-outline" size={24} color="white" /></View></AnimatedTouchable></View> )}const styles = StyleSheet.create({ confirmInputContainer:{ backgroundColor:"#7f96ff", height: 48, flexDirection:"row", paddingVertical:10, justifyContent:"center", }, inputsContainer:{ backgroundColor:"white", height: 48, flexDirection:"row", paddingVertical:10, marginVertical:10, justifyContent:"space-between", }, shadowProp: { shadowColor: '#353935', shadowOffset: {width: -2, height: 4}, shadowOpacity: 0.2, shadowRadius: 4, }, textInputArea:{ width:"60%", maxWidth:"60%", marginLeft:20, }, verticalLine:{ height: "100%", width: 1, backgroundColor: "#909090", marginHorizontal:5, }, currencyInputArea:{ maxWidth:"20%", width:"20%", marginRight:20 },})export default AddAndConfirmExpense;