Implementing swipe-right on a React Native FlatList
August 07, 2017
6 minute read
I’m progressing on my “master-detail” pattern for a react-native app. The actual implementation of master-detail is shockingly simple (more on that later). However, I bumped into some specific issues when I was implementing it. The first of these was covered last time – how to detect orientation changes in React Native. The next is this. How do I implement swipe-right so that I can add a swipe-to-delete function to a FlatList.
Let me explain a little further. The latest edition of React Native has updated the list handling. ListView is (or will be) deprecated. A bunch of new list handling methods come in, among them the FlatList, SectionList and VirtualizedList. This is great. I no longer have to implement a data source. All I have to do is pass the data I want to render to the FlatList and a rendering function and I am done.
There is also another component – the Swipeout – that implements swiping. However, it does it for ListView, which is deprecated. So no joy there. Or so I thought. It’s actually possible, but with a huge set of provisos.
Let’s start with the setup. I have two components – a NoteList.js component that renders the list, and a NoteListItem.js that renders the NoteListItem. I want a few events to fire on the NoteList.js:
onSelectItem(item) will fire when the item is pressed.
onDeleteItem(item) will fire when the item is swipe-right deleted.
onAddItem() will fire when the plus item in the status bar is pressed.
These will then effect a scene change or a data update as needed. There are a couple of rules to keep in mind:
Touchable events must have a native item as their direct child (or pass on setNativeProps).
Swipeout requires the rowId to handle open/close properly (so only one swipe drawer is open at any given time).
The first rule means you need to handle an onPress event in the NoteListItem renderer which is then picked up by the list item. Here is my NoteListItem.js script:
Note how I pass in the onPress event handler (with a default, in case you don’t do this). I also wrap the View (which is a native component) in the TouchableHighlight so that my entire view is clickable. This makes the entire row clickable if we define an onPress event handler.
Let’s take a look at the setup of the NoteList.js class first.
This is all fairly basic FlatList code. If you use a simple application, like this:
Then you should be able to see the three items and click on them. A debug message will appear in the debug console. In addition, you can click on the plus sign in the status bar and get the onAddItem debug message.
Now, let’s consider swiping. All this work is done in the NoteList.js component. I’m going to use react-native-swipeout to handle swiping. Firstly, I need to understand what row is currently showing the swipe-right drawer. To do this, add an activeRow element to the component state:
Next, ensure that the FlatList is re-rendered when the state is changed. This is handled in the FlatList props:
This state is changed on two occasions. Firstly, when the user swipes right, I’m going to call event handler this.onSwipeOpen(). Then, when the swipe-right drawer closes, I’m going to call this.onSwipeClose(). I’ll use these methods to control the state:
The important functionality is within the onSwipeClose() method. This will only reset the state to null (meaning nothing is selected) when the closed item is the currently active element. This is important because Swipeout calls the onClose event handler (which is this method) for all sorts of events, most of which do not actually indicate a new swipe.
Finally, let’s take a look at how this is rendered in the renderItem() method:
The main item to note here is that the rowId is filled in by the index of the data object passed to FlatList, which starts at 0 and goes up from there. The onOpen() and onClose() event handlers are wired to the new onSwipeOpen() and onSwipeClose() methods, and the close flag is set so that the drawer is closed if the active row does not match.
If you implement this code, you will note the following:
Pressing a row calls onSelectItem().
Swiping right will open the swipe-right drawer with the Delete button in it.
Swiping right on another row will cause the original swipe-right drawer to close.
Pressing the delete button calls onDeleteItem().
Hopefully, the next blog post will be about the master-detail pattern in React Native. Until then, I hope this helps implementing awesome lists.
I’ve been working on my UI developer skills recently (and I’ve done a few other posts about this as well). If you look at any of the recent design trends on Dribbble or UPLabs, you will see plenty of rounded corners. SwiftUI makes it simple to create rounded corners on all the corners - just add .cornerRadius(radius) as a modifier to the view.
I’ve got to the point with my template where I am thinking about deployment options. There is already a great option (the gh-pages module) for deploying to a github.io site. However, I am going to be running most of my services on Microsoft Azure. I want to deploy my service automatically and copy the web application to a web site on Azure.
Thus far, in my journey to produce a customized toolchain for my React development, I’ve covered a lot of ground. See part 1 and part 2 for that coverage. I’ve come to the point where I need to discuss testing, which is a complex area.
In my last article, I started a React app from scratch, then integrated Parcel as the bundler, TypeScript for language support, SASS, ESLint for linting the code, and Stylelint for linting the style sheets. I didn’t include any testing capabilities, so that is next on my agenda.