How can I implement drag and drop functionality between two FlatLists in React Native?

59 views Asked by At

I've searched online for solutions but haven't found a clear example or tutorial that fits my requirements. Can someone provide guidance on how to achieve this? Any libraries or approaches that can simplify the implementation would be greatly appreciated.

I need to implement drag and drop functionality between two FlatLists. Specifically, I have two FlatLists displaying different sets of items, and I want the user to be able to drag an item from one FlatList and drop it into the other.

2

There are 2 answers

0
Aakash siddhpura On BEST ANSWER

You can use React Native DraxList and DraxView also GestureHandlerRootView Install with npm :

npm i react-native-drax
npm install react-native-gesture-handler

Example:

import { Image, StatusBar, StyleSheet, Text, TouchableOpacity, View, 
Dimensions, FlatList, ScrollView } from "react-native";
import React, { useState } from 'react';
import { Colors } from "../colors";
import { useNavigation } from "@react-navigation/native";
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { DraxProvider, DraxView, DraxList } from 'react-native-drax';


const Home = () => {
const draggableItemList = [
    {
        "id": 1,
        "name": "EMI Calculators",
        "logo_url": "../images/2.png"
    },
    {
        "id": 2,
        "name": "EMI Calculators",
        "logo_url": "../images/2.png"
    },
    {
        "id": 3,
        "name": "EMI Calculators",
        "logo_url": "../images/2.png"
    },
    {
        "id": 4,
        "name": "EMI Calculators",
        "logo_url": "../images/2.png"
    },
    {
        "id": 5,
        "name": "EMI Calculators",
        "logo_url": "../images/2.png"
    },

];
const FirstReceivingItemList = [
    {
        "id": 5,
        "name": "EMI Calculators",
        "logo_url": "../images/2.png"
    },
    {
        "id": 6,
        "name": "EMI Calculators",
        "logo_url": "../images/2.png"
    },
    {
        "id": 7,
        "name": "EMI Calculators",
        "logo_url": "../images/2.png"
    },
    {
        "id": 8,
        "name": "EMI Calculators",
        "logo_url": "../images/2.png"
    },

];

const [receivingItemList, setReceivedItemList] = React.useState(FirstReceivingItemList);
const [dragItemMiddleList, setDragItemListMiddle] = React.useState(draggableItemList);

const navigation = useNavigation();

const DragUIComponent = ({ item, index }) => {
    return (
        <DraxView
            style={[styles.centeredContent, styles.draggableBox]}
            draggingStyle={styles.dragging}
            dragReleasedStyle={styles.dragging}
            hoverDraggingStyle={styles.hoverDragging}
            dragPayload={index}
            longPressDelay={150}
            key={index}
            receivingStyle={styles.receiving}
            renderContent={({ viewState }) => {
            
                return (
                    <View style={{
                        alignItems: 'center',
                    }}>
                        <TouchableOpacity style={[styles.profileImgContainer, { backgroundColor: Colors.black }]} >
                            <Image source={require('../images/2.png')} style={styles.profileImg} />
                        </TouchableOpacity>

                        <Text style={styles.text1}>{item.name}
                        </Text>
                    </View>
                );
            }}
        >

        </DraxView>
    );
}

const ReceivingZoneUIComponent = ({ item, index }) => {
    return (
        <DraxView
            style={[styles.centeredContent, styles.receivingZone]}
            dragPayload={index}
            receivingStyle={styles.receiving}
            renderContent={({ viewState }) => {
                // const receivingDrag = viewState && viewState.receivingDrag;
                // const payload = receivingDrag && receivingDrag.payload;
                return (
                    <View style={{
                        alignItems: 'center',
                    }}>
                        <TouchableOpacity style={[styles.profileImgContainer, { backgroundColor: Colors.black }]} >
                            <Image source={require('../images/2.png')} style={styles.profileImg} />
                        </TouchableOpacity>

                        <Text style={styles.text1}>{item.name}
                        </Text>
                    </View>
                );
            }}
            key={index}
        
        />
    );
}

const FlatListItemSeparator = () => {
    return (<View style={styles.itemSeparator} />);
}

return (
    <View style={styles.container}>
        <GestureHandlerRootView
            style={{ flex: 1 }}>

            <DraxProvider>
                {/* <ScrollView> */}
                
                    <View style={[styles.receivingContainer, {}]}>
                        <DraxView style={styles.innerLayout}
                         onReceiveDragDrop={(event) => {
                            console.log(event.dragged.payload)
                            let selected_item = dragItemMiddleList[event.dragged.payload];
                            let newReceivingItemList = [...receivingItemList];
                            newReceivingItemList[newReceivingItemList.length] = selected_item;
                            setReceivedItemList(newReceivingItemList);
        
                            const newDragItemMiddleList = [...dragItemMiddleList].filter((item) => item.id !== selected_item.id);
                            console.log(newDragItemMiddleList)
                            setDragItemListMiddle(newDragItemMiddleList);
                        }}>
                            <Text style={styles.headerText}>EMI Calculators</Text>
                            <DraxList
                                data={receivingItemList}
                                renderItemContent={ReceivingZoneUIComponent}
                                keyExtractor={(item, index) => {index.toString()}}
                                ItemSeparatorComponent={FlatListItemSeparator}
                                numColumns={4}
                                scrollEnabled={true}
                            />
                        </DraxView>

                    </View>
                    <DraxView style={styles.innerLayout}
                     onReceiveDragDrop={(event) => {


                        console.log(event.dragged.payload)
                        console.log(receivingItemList)
                        let selected_item = receivingItemList[event.dragged.payload];
                        console.log(selected_item)
                        let newReceivingItemList = [...dragItemMiddleList];
                        newReceivingItemList[newReceivingItemList.length] = selected_item;
                        setDragItemListMiddle(newReceivingItemList);
                        
    
                        const newDragItemMiddleList = [...receivingItemList].filter((item) => item.id !== selected_item.id);
                        console.log(newDragItemMiddleList)
                        setReceivedItemList(newDragItemMiddleList);
                        
                    }}
                    >
                        <Text style={styles.headerText}>Loan</Text>
                        <View style={styles.draxListContainer}>
                            <DraxList
                                data={dragItemMiddleList}
                                renderItemContent={DragUIComponent}
                                keyExtractor={(item, index) => index.toString()}
                                numColumns={4}
                                ItemSeparatorComponent={FlatListItemSeparator}
                                scrollEnabled={true}
                            />
                        </View>
                    </DraxView>
        
            </DraxProvider>
        </GestureHandlerRootView>

    </View>

);

};

const styles = StyleSheet.create({
container: {
    flex: 1,
    backgroundColor: Colors.backGroundColor,
},

innerLayout: {
    marginHorizontal: 10,
    paddingHorizontal:10,
    marginVertical: 5,
    borderRadius: 5,
    backgroundColor: Colors.innerBackGroundColor,
    borderColor: Colors.borderColor,
    borderWidth: 1,
},
innerLayout1: {
    marginStart: 10,
    marginVertical: 5,
    borderRadius: 5,
    backgroundColor: Colors.innerBackGroundColor,
    borderColor: Colors.borderColor,
    borderWidth: 1,
    flex: 1,
    padding: 10,
},
innerLayout2: {
    marginHorizontal: 10,
    marginVertical: 5,
    borderRadius: 5,
    borderColor: Colors.borderColor,
    borderWidth: 1,
    flex: 1,
    padding: 10,
},

headerText: {
    fontFamily: 'groww_sans_medium',
    fontSize: 20,
    color: Colors.white,
    marginVertical: 10,
},

text: {
    fontSize: 10,
    color: Colors.white,
    marginTop: 5,
    marginBottom: 10,
    textAlign: 'center',
    fontFamily: 'groww_sans_ragular'
},
text1: {
    fontSize: 10,
    color: Colors.white,
    marginTop: 5,
    textAlign: 'center',
    fontFamily: 'groww_sans_ragular'
},
profileImgContainer: {
    height: 35,
    width: 35,
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 10,
    backgroundColor: Colors.white
},

profileImg: {
    height: 35,
    width: 35,
    borderRadius: 10,
},

leftContainer: {
    justifyContent: 'flex-start',
    flexDirection: 'row',
    alignItems: 'center',
    marginStart: 10
},
rightContainer: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'flex-end',
    alignItems: 'center',
    marginEnd: 10
},

receivingZone: {
    height: (Dimensions.get('window').width / 4) - 12,
    borderRadius: 10,
    width: (Dimensions.get('window').width / 4) - 12,
    justifyContent: 'center',
    alignItems: 'center',
    marginRight: 5
},
draggableBox: {
    width: (Dimensions.get('window').width / 4) - 12,
    height: (Dimensions.get('window').width / 4) - 12,
    borderRadius: 10,
    justifyContent: 'center',
    alignItems: 'center',
    marginRight: 5
},

});


export default Home;
2
Aqeel Ahmad On

You can use React Native Draggable FlatList Install with npm :

npm install --save react-native-draggable-flatlist

or with yarn:

yarn add react-native-draggable-flatlist

and use by importing:

import DraggableFlatList from 'react-native-draggable-flatlist'

Example:


import React, { useState } from "react";
import { Text, View, StyleSheet, TouchableOpacity } from "react-native";
import DraggableFlatList, {
  ScaleDecorator,
  onDragEnd,
} from "react-native-draggable-flatlist";

const NUM_ITEMS = 10;
function getColor(i: number) {
  const multiplier = 255 / (NUM_ITEMS - 1);
  const colorVal = i * multiplier;
  return `rgb(${colorVal}, ${Math.abs(128 - colorVal)}, ${255 - colorVal})`;
}

type Item = {
  key: string;
  label: string;
  height: number;
  width: number;
  backgroundColor: string;
  listId: string; // new property to identify list
};

const initialDataLeft: Item[] = [...Array(NUM_ITEMS / 2)].map((d, index) => {
  const backgroundColor = getColor(index);
  return {
    key: `left-item-${index}`,
    label: String(index) + "",
    height: 100,
    width: 60 + Math.random() * 40,
    backgroundColor,
    listId: "left", // assign list ID
  };
});

const initialDataRight: Item[] = [...Array(NUM_ITEMS / 2)].map((d, index) => {
  const backgroundColor = getColor(NUM_ITEMS / 2 + index);
  return {
    key: `right-item-${index}`,
    label: String(index) + "",
    height: 100,
    width: 60 + Math.random() * 40,
    backgroundColor,
    listId: "right", // assign list ID
  };
});

export default function App() {
  const [leftData, setLeftData] = useState(initialDataLeft);
  const [rightData, setRightData] = useState(initialDataRight);

  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>
    );
  };

  const handleDragEnd = (fromListId, toListId, item) => {
    const fromList = (fromListId === "left") ? leftData : rightData;
    const toList = (toListId === "left") ? setLeftData : setRightData;

    const updatedFromList = fromList.filter((i) => i.key !== item.key);
    toList((prevData) => [...prevData, item]);
  };

  // Custom DraggableFlatList for each list
  const LeftFlatList = () => (
    <DraggableFlatList
      data={leftData}
      keyExtractor={(item) => item.key}
      renderItem={renderItem}
      onDragEnd={({ data }) => {
        handleDragEnd("left", "right", data[0]); // assuming only 1 item is dragged
        setLeftData(data);
      }}
    />
  );

  const RightFlatList = () => (
    <DraggableFlatList
      data={rightData}
      keyExtractor={(item) => item.key}
      renderItem={renderItem}
      onDragEnd={({ data }) => {
        handleDragEnd("right", "left", data[0]); // assuming only 1 item is dragged
        setRightData(data);
      }}
    />
  );

  return (
    <View style={styles.container}>
      <View style={styles.listContainer}>
        <LeftFlatList />
      </View>
      <View style={styles.listContainer}>
        <RightFlatList />
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  rowItem: {
    height: 100,
    width: 100,
    alignItems: "center",
    justifyContent: "center",
  },
  text: {
    color: "white",
    fontSize: 24,
    fontWeight: "bold",
    textAlign: "center",
  },
});

for more details: Visit documentation