react-native-keyboard-aware-scroll-view
react-native-keyboard-aware-scroll-view copied to clipboard
How to get the best of two worlds: keyboardaware & draggable flatlists
Is it possible to get the draggable functionality from draggableflatlist and the awesome keyboard aware functionality of this repo? Please let me know if there is a way to do this without running into scrolling errors!
I think it's possible, but you would need to use KeyboardAwareHOC and wrap around the draggable flatlist.
As described here.
I'm not sure if it's compatible, so you might have extra steps
@EduVencovsky You sir are a gentleman and a scholar! Been searching for that solution for ages.
If anyone wants to do the same thing, here is my Keyboard Aware Draggable Flatlist
// In keyboard-aware-draggable-flatlist.js
import DraggableFlatList from 'react-native-draggable-flatlist';
import listenToKeyboardEvents from 'react-native-keyboard-aware-scroll-view/lib/KeyboardAwareHOC';
const config = {
enableOnAndroid: true,
enableAutomaticScroll: true,
};
export default listenToKeyboardEvents(config)(DraggableFlatList);
Did this work well for you? Wrapping my flatlist with this HOC doesn't auto-scroll when list items move out of view
It is working well. Fixing the scroll issue was a bit of a pain tho.
It's been awhile since I worked on it so I don't remember everything, but hopefully you can reverse-engineer my code.
<KeyboardAwareDraggableFlatList
data={fields}
onDragEnd={({ from, to }) => {
move(from, to);
}}
keyExtractor={item => item.id}
renderItem={renderItem}
ListFooterComponent={() => (
<AddNewComponent append={append} scrollToIndex={scrollToIndex} />
)}
// eslint-disable-next-line react-native/no-inline-styles
containerStyle={{ marginBottom: errors.name ? 188 : 161, flex: 1 }}
// eslint-disable-next-line react-native/no-inline-styles
contentContainerStyle={{ marginTop: 5 }}
activationDistance={10}
ref={scrollRef}
getItemLayout={getItemLayout.bind(this)}
enableResetScrollToCoords={false}
/>
export const AddNewComponent = ({ append, scrollToIndex }) => {
return (
<TouchableOpacity
style={styles.addComponent}
onPress={() => {
append(new Component());
scrollToIndex();
}}
>
<FontAwesomeIcon icon={faPlus} color={Colors.neutral[4]} size={24} />
<Text style={[styles.readOnly, styles.input]}>Add Component</Text>
</TouchableOpacity>
);
}
const scrollToIndex = () => {
setTimeout(
() => {
if (scrollRef && scrollRef.current) {
scrollRef.current?.scrollToEnd();
}
},
100
);
}
const scrollRef = useRef(null)
const renderItem = useCallback(params => {
return (
<ComponentListItem
control={control}
{...params}
itemRefs={itemRefs}
remove={remove}
handleAllChecked={handleAllChecked}
/>
);
}, [])
const ComponentListItem = ({
item,
index,
drag,
isActive,
control,
itemRefs,
remove,
handleAllChecked,
}) => {
const [snapPointsLeft] = useState([50]);
// Closes other list items after 1second, if you open a new list item. Boilerplate from https://github.com/computerjazz/react-native-draggable-flatlist
useEffect(() => {
if (item.id === 'key-0') {
setTimeout(() => {
itemRefs.current
?.get(item.id)
?.open(OpenDirection.LEFT, undefined, { animated: true });
}, 1000);
}
}, [item.id]);
return (
<ScaleDecorator>
<SwipeableItem
key={item.id}
// item={item}
ref={ref => {
if (ref && !itemRefs.current.get(item.id)) {
itemRefs.current.set(item.id, ref);
}
}}
onChange={({ openDirection }) => {
if (openDirection !== OpenDirection.NONE) {
// Close all other open items
[...itemRefs.current.entries()].forEach(([id, ref]) => {
if (id !== item.id && ref) ref.close();
});
}
}}
overSwipe={OVERSWIPE_DIST}
renderUnderlayLeft={() => (
<UnderlayLeft drag={drag} remove={remove} index={index} />
)}
snapPointsLeft={snapPointsLeft}
>
<View style={styles.componentContainer}>
<TouchableOpacity
onLongPress={drag}
disabled={isActive}
style={[
// eslint-disable-next-line react-native/no-inline-styles
{ backgroundColor: isActive ? Colors.primary[4] : null },
styles.componentContainer,
]}
>
<FontAwesomeIcon
style={styles.gripIcon}
icon={faGripVertical}
color={Colors.neutral[4]}
size={24}
/>
<View style={[styles.inputBox, styles.shadow]}>
<Input
name={`components[${index}].name`}
control={control}
style={{
...styles.input,
}}
defaultValue={item.title}
maxLength={40}
/>
</View>
</TouchableOpacity>
<View style={styles.checkboxContainer}>
<Checkbox
name={`components[${index}].isChecked`}
control={control}
callback={handleAllChecked}
/>
</View>
</View>
</SwipeableItem>
</ScaleDecorator>
);
};
Hopefully that was helpful
@charlestbell - Do you happen to still have the getItemLayout function? Auto-scroll doesn't work when dragging using the above code.
I also encountered the autoscroll missing when moving dragged items at the list edges. Ended up not using the library, for my use case it has been enough this single hook to make the list scroll when keyboard hovers content:
useEffect(() => {
const listener = Keyboard.addListener('keyboardDidShow', (e) => {
const keyboardHeight = e.endCoordinates.height
TextInput.State.currentlyFocusedInput().measure(
(originX, originY, width, height, pageX, pageY) => {
const yFromTop = pageY
const componentHeight = height
const screenHeight = Dimensions.get('window').height
const yFromBottom = screenHeight - yFromTop - componentHeight
const hiddenOffset = keyboardHeight - yFromBottom
const margin = 32
if (hiddenOffset > 0) {
listRef.current?.scrollToOffset({
animated: true,
offset: scrollRef.current.value + hiddenOffset + margin,
})
}
}
)
})
return () => listener.remove()
}, [])
Where scrollRef
is just a ref value to keep track of the scrolling offset, and listRef
reference the DraggableFlatList
item.
const listRef = useRef<FlatList<YourType>>(null)
const scrollRef = useRef({ value: 0 })
...
<DraggableFlatList
ref={listRef}
onScrollOffsetChange={(e) => {
scrollRef.current.value = e
}}
...
Hope this can help someone :)
@vsofroniev I ended up removing that function. I think it was causing issues on Android or something like that. I believe you can make auto scroll work without it, it's just slower.