How to: Create a simple swipeable list in React Native

Fabian Frey
Appdafuer
Published in
5 min readSep 15, 2017

I’m a huge fan of React Native, because it is very easy to implement complex concepts and designs. As an example I want to explain you in detail how to create a very simple swipeable list, like this:

the project on github

Step 1: Create a new list component

Let’s create a new component called SwipeableList. SwipeableList is simply a FlatList, but with the addition of a swipe functionality, which we will manually add.
We pass SwipeableList a style and a data prop. Data is just the list content and looks like this:

const listData = [
{key: '1. element'},
{key: '2. element'},
{key: '3. element'},
];

What else do we need for FlatList? I recommend ItemSeparatorComponent for a clear look and native feeling, but it is optional. Furthermore we want the list not to scroll when the user has started a swipe, so we implement a flag in the state which indicates if the list is scrollable or not. In my example I named it just enable.
This is what the inital state object of SwipeableList looks like, in the beginning the list is scrollable.

this.state = {
enable: true,
data: this.props.data,
};

As mentioned when a swipe has started we need to update the state, so our component rerenders and isn’t scrollable anymore (and the other way round). We do that in function named setScrollEnabled:

setScrollEnabled(enable) {
this.setState({
enable,
});
}

Moreover we want to delete the rows which were swiped by the user in our little example, therefore we define a function which handles the deletion. As you can see success just pops out the row information of the data array and updates the state.
But you don’t need to do a deletion. This is the function which will be called right after the swipe animation is done, so you can get very creative here ;-).

success(key) {
const data = this.state.data.filter(item => item.key !== key);
this.setState({
data,
});
}

So now we can implement the render method which actually only returns the FlatList component.

For sure you’ve noticed that the renderItem method is missing in our SwipeableList class. In my opinion it’s good practice to create another component which represents the ListItem and implements the PanResponder and the swipe animation to every row in our list. So the renderItem function looks like this.

As props we pass the text which will be displayed centered in the row, a function success which is a completion handler for the swipe animation and finally the function to enable and disable scrolling of our list.

Step 2: Create the ListItem component

We create another component called ListItem. Here is where the magic starts.
I want to beginn with the render method:

The first trick is the style of the first view (which I will call ListItemView now). It has a negative marginLeft.

listItem: {
height: 80,
marginLeft: -100,
justifyContent: 'center',
backgroundColor: 'red',
},

After that we add an animated view, this is the view which can be touched and swiped. On the left side is an absolute view which first is hidden because of the negative marginLeft of the ListItemView and secondly its width is exactly the margin of the first view (100 in our example).

absoluteCell: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
width: 100,
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
},

On the right side of the ListItemView is another view. Its width is exactly the width of the device and it has a positive marginLeft of 100. So we make sure the whole view is visible.

innerCell: {
width: width,
height: 80,
marginLeft: 100,
backgroundColor: 'yellow',
justifyContent: 'center',
alignItems: 'center',
},

Step 3: Integration the PanResponder

In the constructor of our ListItem class we create the PanResponder including handlers provided by the gesture responder system. I recommend you to read the React Native documentation for more information on the gesture responder system.
In short: There are two methods asking the view if it wants to become the touch responder. The first one is onStartShouldSetPanResponder. In our case this method will always return false, because the view should only become responder on a move, not on the start of a touch. Therefore the second method onMoveShouldSetPanResponder returns true.
There is a third method called onPanResponderTerminationRequest which handles the case when another view wants to become responder. Because we don’t want the view to release the responder we always return false.
In addition we use onPanResponderMove (which is called when an user swipes and is moving the view) and onPanResponderRelease (which is called when when the user has released all touches).

onPanResponderMove: We don’t want the swipe animation to start right away. Why? Imagine the user wants to scroll the list but scrolls not percisely up/down, the swipe gesture would start and the FlatList would stop to scroll. That is not the behaviour we like to have. So we determine that the user has to scroll a bit in x axis direction before we stop the FlatList to scroll (setScrollViewEnabled) and start the swipe animation (with Animated.timing) by setting a new x coordinate to the view.

onPanResponderRelease: In general we want an automatic finishing swipe animation after the user swiped and released the view. Therefore we set the new x coordinate of the position to the width of the device. For animation we’re setting up a duration of 300ms.
But when the user swiped and released the view again, not passing a certain distance, we want the absolute view to hide again, setting the x position to 0.

By calling the success method we update our data array. Afterwards we enable scrolling the FlatList by calling setScrollViewEnabled.

setScrollViewEnabled(enabled) {
if (this.scrollViewEnabled !== enabled) {
this.props.setScrollEnabled(enabled);
this.scrollViewEnabled = enabled;
}
}

We compare the parameter enabled with the property scrollViewEnabled so that we don’t call the method this.props.setScrollEnabled all the time. Remember, calling setScrollEnabled causes a rerendering of the SwipeableList. So the comparison makes sure we just render SwipeableList again when there is change in the scrolling behaviour.

And that’s it. Congratulations, you’ve just created a very simple and customizable swipeable list.

I hope you’ve enjoyed reading.
If you have any question don’t hesitate to ask and check out the project on my github.

--

--