Create Swipeable Gestures for cross-platform React Native Apps

2019-12-19

React Native's built-in touch system Gesture Responder system has performance limitations on both iOS and Android platforms. To overcome this and a few other problems with the built-in Gesture system, there is an open-source library that you can use to create awesome gestures in your React Native apps.

The library react-native-gesture-handler not only overcomes the performance issue on each native platform, but it also uses touchables that run in the native thread and follow default platform behavior. In other words, it uses native support to handle gestures. There are many gestures supported by this library at the moment, but let us take a look at one of the most useful ones: Swipeable.

Here is the link to the Github repo with the complete code used in this tutorial.

Getting Started

To get started, create a bare react native project using react-native CLI by running the below commands from a terminal window.

1react-native init swipeableGestures
2
3# after the project directory is created
4# and dependencies are installed
5cd swipeableGestures

Since you are at the initial stage of the demo, let us set up the main list to render. This list is going to have three items that will cover the different and common usage with Swipeable component you can add in your React Native app. Most of these use cases, you are already using in modern apps both on iOS and Android platform.

Open, App.js, and add the following content. The following is just a simple flatList component that renders data items from an array of mock data named mockDataList outside the App component.

1import React from 'react'
2import {
3 SafeAreaView,
4 StyleSheet,
5 View,
6 Text,
7 StatusBar,
8 FlatList
9} from 'react-native'
10
11const mockDataList = [
12 { id: '1', text: 'Swipe me left!' },
13 { id: '2', text: 'Swipe me right!' },
14 { id: '3', text: 'Try swiping in both directions' }
15]
16
17const Separator = () => <View style={styles.itemSeparator} />
18
19const ListItem = ({ text }) => (
20 <View style={{ paddingVertical: 20 }}>
21 <Text style={{ fontSize: 24 }}>{text}</Text>
22 </View>
23)
24
25const App = () => {
26 return (
27 <>
28 <StatusBar barStyle="dark-content" />
29 <SafeAreaView style={styles.container}>
30 <FlatList
31 data={mockDataList}
32 keyExtractor={item => item.id}
33 renderItem={({ item }) => <ListItem {...item} />}
34 ItemSeparatorComponent={() => <Separator />}
35 />
36 </SafeAreaView>
37 </>
38 )
39}
40
41const styles = StyleSheet.create({
42 container: {
43 flex: 1
44 },
45 itemSeparator: {
46 flex: 1,
47 height: 1,
48 backgroundColor: '#444'
49 }
50})
51
52export default App

To run the demo app, depending on your OS, run one command from below:

1# for macOS
2react-native run-ios
3
4# for windows/linux
5react-native run-android

The above code will render the following output:

ss1

Adding react-native-gesture-handler

The react-native-gesture-handler supports both react-native CLI projects and Expo projects. To install it, execute the below command:

1yarn add react-native-gesture-handler

For the current demo, since you are using react-native CLI, only Android users have to add the following configuration MainActivity.java file.

1package com.swipegesturesdemo;
2
3import com.facebook.react.ReactActivity;
4import com.facebook.react.ReactActivityDelegate;
5import com.facebook.react.ReactRootView;
6import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
7
8public class MainActivity extends ReactActivity {
9
10 /**
11 * Returns the name of the main component registered from JavaScript. This is used to schedule
12 * rendering of the component.
13 */
14 @Override
15 protected String getMainComponentName() {
16 return "swipeGesturesDemo";
17 }
18
19 @Override
20 protected ReactActivityDelegate createReactActivityDelegate() {
21 return new ReactActivityDelegate(this, getMainComponentName()) {
22 @Override
23 protected ReactRootView createRootView() {
24 return new RNGestureHandlerEnabledRootView(MainActivity.this);
25 }
26 };
27 }
28}

For iOS users, navigate inside ios/ directory from the terminal and run pod install.

Everything is set up, all you have to do is run the build command again, such as for iOS: react-native run-ios.

You won't see anything new changes inside the app for now, but if it renders correctly as our previous step that means everything is working as expected.

The Swipeable Component

To implement swipeable rows, start by importing Swipeable component in App.js file.

1import Swipeable from 'react-native-gesture-handler/Swipeable'

This component is going to wrap to the component that is going to handle actions based on swipe gestures. Looking back at the App.js file, it is the View rendered by the ListItem component.

1const ListItem = ({ text }) => (
2 <Swipeable renderLeftActions={LeftActions}>
3 <View style={{ paddingVertical: 20 }}>
4 <Text style={{ fontSize: 24, paddingHorizontal: 10 }}>{text}</Text>
5 </View>
6 </Swipeable>
7)

The Swipeable has many properties including the one used in the above code snippet: renderLeftActions. It is a method that returns an action panel when the user swipes right on the list item. One the swipe, the action reveals.

To make it look more useful, let us define the custom component LeftActions.

1const LeftActions = () => {
2 return (
3 <View
4 style={{ flex: 1, backgroundColor: 'blue', justifyContent: 'center' }}
5 >
6 <Text
7 style={{
8 color: 'white',
9 paddingHorizontal: 10,
10 fontWeight: '600'
11 }}
12 >
13 Left Action
14 </Text>
15 </View>
16 )
17}

Below is the demo of what you have done so far:

ss2

Animating the Swipeable Component

Using React Native's Animated API, you can add animations. LeftActions has two arguments:

  • progress: the amount that it has been swiped
  • dragX: this determines how far the drag happens

Using interpolation you can scale the values of the input and the output range. An interpolation maps input and output ranges using linear interpolation. Using extrapolate property together with input and output ranges, the curve would not go beyond the two ranges provided.

Here is the complete code snippet for LeftActions component so far.

1// do not forget to import Animated from 'react-native'
2
3import {
4 SafeAreaView,
5 StyleSheet,
6 View,
7 Text,
8 StatusBar,
9 FlatList,
10 // add this
11 Animated
12} from 'react-native'
13
14// LeftActions code snippet
15
16const LeftActions = (progress, dragX) => {
17 const scale = dragX.interpolate({
18 inputRange: [0, 100],
19 outputRange: [0, 1],
20 extrapolate: 'clamp'
21 })
22 return (
23 <View
24 style={{ flex: 1, backgroundColor: 'blue', justifyContent: 'center' }}
25 >
26 <Animated.Text
27 style={{
28 color: 'white',
29 paddingHorizontal: 10,
30 fontWeight: '600',
31 transform: [{ scale }]
32 }}
33 >
34 Left Action
35 </Animated.Text>
36 </View>
37 )
38}

The value provided to the input range is in pixels. Here is the demo:

ss3

Without providing the extrapolate property, the result is not going to be as expected or as in the above demo. It will keep on enlarging the text. Here is a demo without using the property.

ss4

Building the Swipeable component from right

Since you have gone through the basics of what LeftActions does and combining it with Animated provides a useable feature.

In emailing apps or messaging apps like WhatsApp, you might have that when you swipe right there are often two actions coupled together. Using the same knowledge, try to create it own the right swipe. First, provide the renderRightActions which is similar in the concept but works exactly opposite to renderRightActions.

1<Swipeable renderLeftActions={LeftActions} renderRightActions={RightActions}>
2 {/*... Rest of the code remains same*/}
3</Swipeable>

Next, create a RightActions component with the same set of arguments required for the Animated API. There are two actions to be defined with their own set of View components.

1const RightActions = (progress, dragX) => {
2 const scale = dragX.interpolate({
3 inputRange: [-100, 0],
4 outputRange: [0.7, 0]
5 })
6 return (
7 <>
8 <View style={{ backgroundColor: 'red', justifyContent: 'center' }}>
9 <Animated.Text
10 style={{
11 color: 'white',
12 paddingHorizontal: 10,
13 fontWeight: '600',
14 transform: [{ scale }]
15 }}
16 >
17 Delete
18 </Animated.Text>
19 </View>
20 <View style={{ backgroundColor: 'green', justifyContent: 'center' }}>
21 <Animated.Text
22 style={{
23 color: 'white',
24 paddingHorizontal: 10,
25 fontWeight: '600',
26 transform: [{ scale }]
27 }}
28 >
29 Archive
30 </Animated.Text>
31 </View>
32 </>
33 )
34}

Here is the demo for the right actions.

ss5

Do you notice any difference in the way they scale? The inputRange and the outputRange values are now reversed since the user is swiping from right to left.

Adding actions on the touch

To make the right actions touchable, let us import the TouchableOpacity from react-native.

1import {
2 //...
3 TouchableOpacity
4} from 'react-native'

Next, wrap both the views in RightActions with its on TouchableOpacity component and for now, pass on an alert message to indicate which button is being pressed.

1const RightActions = (progress, dragX) => {
2 const scale = dragX.interpolate({
3 inputRange: [-100, 0],
4 outputRange: [0.7, 0]
5 })
6 return (
7 <>
8 <TouchableOpacity onPress={() => alert('Delete button pressed')}>
9 <View
10 style={{ flex: 1, backgroundColor: 'red', justifyContent: 'center' }}
11 >
12 <Animated.Text
13 style={{
14 color: 'white',
15 paddingHorizontal: 10,
16 fontWeight: '600',
17 transform: [{ scale }]
18 }}
19 >
20 Delete
21 </Animated.Text>
22 </View>
23 </TouchableOpacity>
24 <TouchableOpacity onPress={() => alert('Archive button pressed')}>
25 <View
26 style={{
27 flex: 1,
28 backgroundColor: 'green',
29 justifyContent: 'center'
30 }}
31 >
32 <Animated.Text
33 style={{
34 color: 'white',
35 paddingHorizontal: 10,
36 fontWeight: '600',
37 transform: [{ scale }]
38 }}
39 >
40 Archive
41 </Animated.Text>
42 </View>
43 </TouchableOpacity>
44 </>
45 )
46}

Here is the demo:

ss6

Conclusion

Swipeable is a useful component to start when it comes to using react-native-gesture-handler library. I often find this library so useful, that I recommend you to go through its official documentation and methods and try other gestures as well.

Originally published at Jscrambler

I'm Aman working as an independent fullstack developer with technologies such as Node.js, ReactJS, and React Native. I try to document and write tutorials to help JavaScript, Web and Mobile developers.