How to Create A Draggable Items List in React Native
Published On: 2023-12-20
Posted By: Harish
React Native flatlist does not have default drag and rearrange capabilities. So in this post we will be using an npm package react-native-draggable-flatlist to make our list items draggable.
RNDF package depends on react-native-animated and react-native-gesture-handler packages.
First we will create a new react-native project and install required dependencies.
Create A New Project
Create a new react-native project by using npx. Check documentation for creating a new react native project.
npx react-native@latest init ReactNativeDraggableFlatlistExample
Install react-native-reanimated package
Go to the project folder and install react-native-reanimated npm package using yarn or npm.
npm install react-native-reanimated
#or
yarn add react-native-reanimated
And add 'react-native-reanimated/plugin' to babel.config.js plugins array. If you don't find a plugin's array, add it.
//babel.config.js
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
...
'react-native-reanimated/plugin', //Have to be at last of the array
],
};
Install react-native-gesture-handler package
Install react-native-gesture-handler package using yarn or npm.
npm install --save react-native-gesture-handler
#or
yarn add react-native-gesture-handler
After installation, wrap the entry point with <GestureHandlerRootView>
. As the name suggests, it has to be the root Higher Order Component (HOC). For newly created projects, its app.tsx.
//app.tsx
import { GestureHandlerRootView } from 'react-native-gesture-handler';
export default function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
...other content like navigation stack etc...
</GestureHandlerRootView>
);
}
You can check the installation process here.
Install react-native-drraggable-flatlist package
Now its time to install the main package, react-native-draggable-flatlist.
npm install --save react-native-draggable-flatlist
#or
yarn add react-native-draggable-flatlist
Don't forget to pod install
for iOS.
Creating a list
For draggable list, we have to pass list data to <DraggableFlatList>
component and update the same list on rearranging. Each item component will be wrapped in <TouchableOpacity>
to get press events. Here, we will use the onLongPress event.
Initialize data
We will initialize example data.
//app.tsx
type Item = {
label: string;
key: string;
backgroundColor: string;
};
const initialData: Item[] = [
{
label: '🍎 Apple',
key: 'apple',
backgroundColor: 'indianred'
},
{
label: '🍊 Orange',
key: 'orange',
backgroundColor: 'orange'
},
{
label: '🍌 Banana',
key: 'banana',
backgroundColor: 'yellowgreen'
},
{
label: '🍇 Grapes',
key: 'grapes',
backgroundColor: 'purple'
}
]
Create an useState constant to set data and list item compoent to render. useState constant has to be updated with new arranged list to trigger a re-render with new list.
//app.tsx
import { useState } from "react";
import { Text, StyleSheet, TouchableOpacity } from "react-native";
export default function App() {
const [data, setData] = useState(initialData);
const renderItem = ({ item, drag, isActive }: RenderItemParams<Item>) => {
return (
<ScaleDecorator>
<TouchableOpacity
onLongPress={drag}
disabled={isActive}
style={[
styles.rowItem,
{
backgroundColor: isActive
? "red"
: item.backgroundColor
}
]}
>
<Text style={styles.text}>{item.label}</Text>
</TouchableOpacity>
</ScaleDecorator>
);
};
return (
<GestureHandlerRootView style={{ flex: 1 }}>
...content...
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
rowItem: {
flexGrow: 1,
height: 100,
alignItems: "center",
justifyContent: "center",
},
text: {
color: "white",
fontSize: 24,
fontWeight: "bold",
textAlign: "center",
},
});
renderItem component is wrapped with a <ScaleDecorator>
cell decorator to notify the user on selection of an item. Scaledecorator makes selected item to scale, <ShadowDecorator>
drops a show around the item on selection and <OpacityDecorator>
changes opacity on selection.
Add flatlist component
For the final step, add the <DraggableFlatList>
component.
<DraggableFlatList
data={data}
onDragEnd={({ data }) => setData(data)}
keyExtractor={(item) => item.key}
renderItem={renderItem}
containerStyle={[{ backgroundColor: 'pink' }]}
/>
We can set list container styles with containerStyle
prop.
This is a vertical list example. If you want to have a draggable horizontal list, wrap the <DraggableFlatList>
component inside <View>
tag and mention horizontal prop.
//For horizontal list
<View>
<DraggableFlatList
horizontal
data={data}
onDragEnd={({ data }) => setData(data)}
keyExtractor={(item) => item.key}
renderItem={renderItem}
containerStyle={[{ backgroundColor: 'pink' }]}
/>
</View>
Final code will look like,
//app.tsx
import { useState } from "react";
import { Text, View, StyleSheet, TouchableOpacity } from "react-native";
import DraggableFlatList, {
RenderItemParams,
ScaleDecorator,
} from "react-native-draggable-flatlist";
type Item = {
label: string;
key: string;
backgroundColor: string;
};
const initialData: Item[] = [
{
label: '🍎 Apple',
key: 'apple',
backgroundColor: 'indianred'
},
{
label: '🍊 Orange',
key: 'orange',
backgroundColor: 'orange'
},
{
label: '🍌 Banana',
key: 'banana',
backgroundColor: 'yellowgreen'
},
{
label: '🍇 Grapes',
key: 'grapes',
backgroundColor: 'purple'
},
]
export default function App() {
const [data, setData] = useState(initialData);
const renderItem = ({ item, drag, isActive }: RenderItemParams<Item>) => {
return (
<ScaleDecorator>
<TouchableOpacity
onLongPress={drag}
disabled={isActive}
style={[
styles.rowItem,
{
backgroundColor: isActive
? "red"
: item.backgroundColor
}
]}
>
<Text style={styles.text}>{item.label}</Text>
</TouchableOpacity>
</ScaleDecorator>
);
};
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<DraggableFlatList
data={data}
onDragEnd={({ data }) => setData(data)}
keyExtractor={(item) => item.key}
renderItem={renderItem}
containerStyle={[{ backgroundColor: 'pink' }]}
/>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
rowItem: {
flexGrow: 1,
height: 100,
alignItems: "center",
justifyContent: "center",
},
text: {
color: "white",
fontSize: 24,
fontWeight: "bold",
textAlign: "center",
},
});

