Search code examples
androidiosreact-nativetouchgestures

Trying to swipe away items in react-native FlatList but not getting onResponderTerminationRequest events


I'm using react-native I have a FlatList that displays a sequence of notifications. I use the built-in Gesture Responder System to allow the user to swipe away notifications they are no longer interested in.

My list items work like so...

<View style={[
            props.style,
            this.state.is_swipeing?{opacity: 0.5}:{},
            (this.state.swipe_direction=="RIGHT")?{backgroundColor:"#CCCCCC"}:{},
        ]}
        onStartShouldSetResponder={(evt)=>{
            console.log('onStartShouldSetResponder');
            return true;
        }}
        onResponderTerminationRequest={(evt)=>{
            console.log('onResponderTerminationRequest');
            if (this.state.swipe_direction == "RIGHT" || this.state.swipe_direction == "LEFT") {
                console.log("dont let go!!!");
                return false;
            }
            console.log("give it up");
            return true;
        }}
        onResponderTerminate={(evt)=>{
            console.log('onResponderTerminate');
            this.setState({is_swipeing: false, start_x: null, start_y: null, swipe_direction: null });
        }}
        onResponderGrant={(evt)=>{
            console.log('onResponderGrant');
            this.setState({is_swipeing: true, start_x: evt.nativeEvent.locationX, start_y: evt.nativeEvent.locationY, swipe_direction: null });
        }}
        onResponderMove={(evt)=>{
            var dx = evt.nativeEvent.locationX - this.state.start_x;
            var dy = evt.nativeEvent.locationY - this.state.start_y;
            if (Math.abs(dx) > Math.abs(dy)) { // big delta X
                if (dx > 40) { // swiping right
                    if (this.state.swipe_direction != "RIGHT") {
                        console.log("Swipeing right");
                        this.setState({ swipe_direction: "RIGHT"});
                    }
                }
                else if (dx < -40) { //swiping left
                    if (this.state.swipe_direction != "LEFT") {
                        console.log("Swipeing left");
                        this.setState({ swipe_direction: "LEFT"});
                    }
                }
                else {
                    if (this.state.swipe_direction != null) {
                        console.log("Not swipeing");
                        this.setState({ swipe_direction: null});
                    }
                }
            }
            else if (Math.abs(dy) > Math.abs(dx)) { // big delta Y
                if (dy > 40) { // swiping down
                    if (this.state.swipe_direction != "DOWN") {
                        console.log("Swipeing down");
                        this.setState({ swipe_direction: "DOWN"});
                    }
                }
                else if (dy < -40) { //swiping up
                    if (this.state.swipe_direction != "UP") {
                        console.log("Swipeing up");
                        this.setState({ swipe_direction: "UP"});
                    }
                }
                else {
                    if (this.state.swipe_direction != null) {
                        console.log("Not swipeing");
                        this.setState({ swipe_direction: null});
                    }
                }
            }
        }}
        onResponderRelease={(evt)=>{
            switch (this.state.swipe_direction) {
                case "UP": {
                    if (props.onSwipeUp) {
                        props.onSwipeUp();
                    }
                    break;
                }
                case "DOWN": {
                    if (props.onSwipeDown) {
                        props.onSwipeDown();
                    }
                    break;
                }
                case "LEFT": {
                    if (props.onSwipeLeft) {
                        props.onSwipeLeft();
                    }
                    break;
                }
                case "RIGHT": {
                    if (props.onSwipeRight) {
                        props.onSwipeRight();
                    }
                    break;
                }
                default: {
                    if (props.onPress) {
                        props.onPress();
                    }
                    break;
                }
            }
            this.setState({is_swipeing: false, start_x: null, start_y: null, swipe_direction: null });
        }}
    >
        {props.children}
    </View>

This all works great, until I have enough items in my list that the list becomes scrollable.

Once the list is long enough to be scrollable, as I swipe left or right, if I move up or down at all, then the FlatList steals the responder tracking, and I get the onResponderTermination event. Making it almost impossible for a user to swipe away an item in the list.

I would expect (based on the documentation referenced above) that I should be getting a onResponderTerminationRequest event, asking if I'm willing to yield the touch. But that event never gets fired.

My question is ... is there any way for me to hold on to that swipe so that I can determine whether or not the delta Y was significant enough to start scrolling before yielding control?

Or, is there another way to approach this that feels like a nice scrollable list, but still allows swiping left/right without short circuiting gestures?

I'm sure this has issues on android VMs, and all android physical devices I have at my disposal. I haven't confirmed if it is also a problem on IOS devices, or simulators.


Solution

  • There is a library called https://github.com/gitboss2000/react-native-swipeable-flat-list

    i hope it could work better for you, than your component. their implementation not so complicated maybe you can fix your component by that.