-
Notifications
You must be signed in to change notification settings - Fork 201
Description
When the keyboard opens, the dropdown is pushed up as expected. But once the keyboard is dismissed, the dropdown doesn’t return to its original position—it just stays higher up on the screen. The issue isn’t related to the margin animation or the dropdown’s open/close state. It seems like the component isn’t reacting properly to the keyboard hiding event. Tried listening to keyboardDidHide and triggering a reposition or animation manually, but it doesn’t help—nothing moves it back down.
It feels like the view doesn’t get notified that the keyboard’s gone, or it’s not re-rendering correctly based on the layout changes.
Code:
`import React, { useState, useRef, useCallback, useEffect } from 'react';
import { View, Image, ImageBackground, TextInput, TouchableOpacity, StyleSheet, Animated, Keyboard, Platform } from 'react-native';
import LottieView from 'lottie-react-native';
import { Dropdown } from 'react-native-element-dropdown';
import ModalNoWifi from './ModalNoWifi';
import MyText from './MyText';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import { COLORS } from '../utils/colors';
const ImageView = ({
image,
bgLoad,
setBgLoad,
setActiveTimer,
isConnected,
setIsConnected,
setImage,
saveImage,
residuos,
name,
setName,
tipo,
setTipo,
color,
setColor,
cantidad,
setCantidad,
objects_shortV2,
addTrash,
}) => {
const [marginTop] = useState(new Animated.Value(15));
const dropdownRef = useRef(null);
const [dropdownOpen, setDropdownOpen] = useState(false);
const hasOpenedRef = useRef(false);
const animateMarginTop = (value) => {
return new Promise((resolve) => {
Animated.timing(marginTop, {
toValue: value,
duration: 1000,
useNativeDriver: false,
}).start(() => resolve());
});
};
const handleDropdownOpen = useCallback(async () => {
if (hasOpenedRef.current || dropdownOpen) return;
hasOpenedRef.current = true;
await animateMarginTop(115);
dropdownRef.current?.open();
setDropdownOpen(true);
}, []);
const handleDropdownClose = useCallback(async () => {
await animateMarginTop(15);
if (dropdownRef.current?.isOpen) {
dropdownRef.current.close();
}
setDropdownOpen(false);
hasOpenedRef.current = false;
}, []);
return (
<View style={{ flex: 1, justifyContent: 'flex-end' }}>
{bgLoad ? (
<ImageBackground
source={{ uri: image }}
style={styles.background}
resizeMode="cover"
onLoad={() => setActiveTimer(true)}
>
<LottieView
autoPlay
speed={1}
loop
style={styles.lottieAnimationIA}
source={require('../../assets/animations/CargarIA.json')}
/>
</ImageBackground>
) : (
<ImageBackground
source={{ uri: image }}
style={styles.background}
resizeMode="cover"
>
{!isConnected ? (
<ModalNoWifi
visible={!false}
onRetry={() => {
NetInfo.fetch().then(state => {
setIsConnected(state.isConnected);
});
setBgLoad(true);
setActiveTimer(false);
}}
onCancel={() => {
setImage(null);
setBgLoad(true);
setActiveTimer(false);
}}
onSaveForLater={() => {
saveImage();
setImage(null);
setBgLoad(true);
setActiveTimer(false);
}}
/>
) : (
<View style={styles.containerIA}>
<MyText fontWeightParam={'medium'} style={{ marginLeft: 5, fontSize: 15 }}>
Objetos detectados en la imagen
</MyText>
<View style={styles.gridConteiner}>
<View style={styles.column1}>
<View style={styles.imageContainer}>
<Image
source={{ uri: image }}
style={styles.imageTrash}
/>
</View>
</View>
<View style={styles.column2}>
<View style={styles.row1}>
<View
style={[styles.colorCircle, { backgroundColor: color }]}
/>
<MyText fontWeightParam={'medium'} style={{ selfAlign: 'center' }}>
{tipo}
</MyText>
</View>
<View style={styles.row2}>
<Dropdown
ref={dropdownRef}
style={styles.dropdown}
data={residuos}
mode='auto'
labelField="label"
valueField="value"
value={name}
search
placeholder={
<MyText fontWeightParam="regular" style={{ fontSize: 14, color: '#C6C6C6' }}>
Elije tu residuo...
</MyText>}
searchPlaceholder="Buscar residuo..."
dropdownPosition="bottom"
inputSearchStyle={styles.searchContainer}
selectedTextStyle={{ fontSize: 12 }}
containerStyle={styles.dropdownContainer}
onFocus={handleDropdownOpen}
onBlur={handleDropdownClose}
onChange={(item) => {
setName(item.value);
setTipo(item.tipo);
setColor(item.color);
}}
autoScroll={false}
renderItem={(item) => (
<View style={styles.itemContainer}>
<MyText style={[styles.itemText, { flex: 5 }]}>{item.label}</MyText>
<View style={{ flex: 5, flexDirection: 'row', alignItems: 'flex-start' }}>
<View style={[
styles.colorCircle,
{ backgroundColor: item.color || 'black' },
]} />
<MyText style={styles.itemTipo}>{item.tipo}</MyText>
</View>
</View>
)}
/>
<TextInput
defaultValue={'1'}
value={cantidad}
style={styles.numberInput}
onChangeText={(t) => setCantidad(t)}
keyboardType="numeric"
/>
</View>
</View>
</View>
<View style={{ alignItems: 'flex-end' }}>
<Animated.View style={{ alignItems: 'flex-end', marginTop }}>
<TouchableOpacity
style={[styles.guardarButton]}
accessibilityLabel="Guardar Residuo"
onPress={() => {
const selectedItem = objects_shortV2.find((obj) => obj.name === name);
if (!selectedItem) return;
const itemToSave = {
...selectedItem,
quantity: Number(cantidad),
observation: '',
weight: 0,
};
addTrash(itemToSave);
setCantidad('1');
setBgLoad(true);
setActiveTimer(false);
}}
>
<MaterialIcons name="save" size={20} color="white" />
<MyText fontWeightParam={'regular'} style={{ color: 'white', marginLeft: 5 }}>
Guardar
</MyText>
</TouchableOpacity>
</Animated.View>
</View>
</View>
)}
</ImageBackground>
)}
</View>
);
};
const styles = StyleSheet.create({
background: {
flex: 1,
width: '100%',
height: '100%',
justifyContent: 'flex-end',
alignItems: 'center',
},
lottieAnimationIA: {
position: 'absolute',
alignSelf: 'center',
bottom: '40%',
width: 125,
height: 125,
},
containerIA: {
width: '100%',
paddingVertical: 15,
backgroundColor: 'white',
borderTopLeftRadius: 25,
borderTopRightRadius: 25,
paddingHorizontal: 15,
},
gridConteiner: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
marginTop: 10,
},
column1: {
width: '20%',
alignItems: 'center',
justifyContent: 'center',
},
column2: {
width: '80%',
flexDirection: 'column',
},
row1: {
flexDirection: 'row',
width: '100%',
alignItems: 'flex-start',
alignContent: 'center',
verticalAlign: 'bottom',
},
row2: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
},
imageContainer: {
flexDirection: "row",
overflow: "visible",
marginRight: 0,
alignItems: 'right',
paddingHorizontal: -25,
marginBottom: 5,
marginTop: 15,
},
imageTrash: {
width: 50,
height: 50,
borderRadius: 25,
marginLeft: -10,
borderWidth: 2,
borderColor: "#fff",
},
dropdown: {
borderWidth: 1,
borderColor: '#E0E0E0',
borderRadius: 10,
paddingHorizontal: 10,
backgroundColor: 'white',
flexGrow: 1,
paddingVertical: 5,
},
dropdownContainer: {
marginTop: 5,
borderRadius: 10,
backgroundColor: 'white',
elevation: 5,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 5,
},
searchContainer: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 5,
backgroundColor: 'white',
margin: 10,
borderRadius: 5,
fontSize: 13,
},
itemContainer: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 10,
paddingHorizontal: 15,
borderBottomWidth: 1,
borderColor: '#e0e0e0',
},
itemText: {
fontSize: 12,
flex: 1,
},
itemTipo: {
fontSize: 12,
textAlign: 'left',
},
colorCircle: {
borderWidth: 0.2,
borderColor: '#000000',
width: 15,
height: 15,
borderRadius: 7.5,
marginHorizontal: 10,
alignSelf: 'center',
alignItems: 'center',
justifyContent: 'center',
},
numberInput: {
padding: 5,
width: '20%',
height: '100%',
textAlign: 'center',
alignSelf: 'center',
borderWidth: 1,
borderColor: '#E0E0E0',
borderRadius: 10,
backgroundColor: 'white',
marginLeft: 10,
fontSize: 18,
},
guardarButton: {
flexDirection: "row",
backgroundColor: COLORS.secondary,
borderRadius: 100,
marginBottom: 15,
paddingHorizontal: 20,
paddingVertical: 5,
justifyContent: "center",
},
});
export default ImageView;
`
Images