You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React Native(Expo Go)中实现可拖拽排序的交互式列表?

在Expo Go React Native中实现可拖拽排序列表的可行方案

方案一:使用react-native-draggable-flatlist(适配Expo最新版本)

这个库目前维护状态良好,适配最新的React Native和Expo版本,是实现拖拽排序的首选方案之一。

步骤:

  1. 安装依赖:

    npx expo install react-native-draggable-flatlist react-native-gesture-handler react-native-reanimated
    

    注:react-native-gesture-handler和react-native-reanimated是必备依赖,Expo环境下用expo install可自动完成版本适配。

  2. 基本使用示例:

    import React, { useState } from 'react';
    import { View, Text, StyleSheet } from 'react-native';
    import DraggableFlatList, { ScaleDecorator } from 'react-native-draggable-flatlist';
    
    const initialData = Array.from({ length: 10 }).map((_, index) => ({
        key: `item-${index}`,
        label: `Item ${index + 1}`,
        backgroundColor: `hsl(${index * 36}deg, 80%, 70%)`,
    }));
    
    export default function DraggableList() {
        const [data, setData] = useState(initialData);
    
        return (
            <DraggableFlatList
                data={data}
                onDragEnd={({ data: newData }) => setData(newData)}
                keyExtractor={(item) => item.key}
                renderItem={({ item, drag, isActive }) => (
                    <ScaleDecorator>
                        <View
                            style={[styles.item, { backgroundColor: item.backgroundColor }]}
                            onLongPress={drag}
                        >
                            <Text style={styles.text}>{item.label}</Text>
                        </View>
                    </ScaleDecorator>
                )}
            />
        );
    }
    
    const styles = StyleSheet.create({
        item: {
            height: 80,
            marginHorizontal: 16,
            marginVertical: 8,
            borderRadius: 8,
            justifyContent: 'center',
            alignItems: 'center',
        },
        text: {
            fontSize: 18,
            fontWeight: 'bold',
            color: 'white',
        },
    });
    

    说明:

    • ScaleDecorator提供拖拽时的缩放动画,也可自定义其他动画效果
    • 支持通过onLongPress或onPressIn触发拖拽
    • onDragEnd回调返回排序后的新数据,直接更新state即可保存顺序

方案二:手动使用react-native-reanimated + react-native-gesture-handler实现(高自定义性)

若对第三方库有顾虑,或需要高度自定义交互逻辑,可基于FlatList手动实现:

步骤:

  1. 安装依赖:

    npx expo install react-native-gesture-handler react-native-reanimated
    
  2. 核心思路:

    • 用GestureDetector和PanGestureHandler监听拖拽手势
    • 通过Reanimated计算拖拽位置,动态调整列表项的位置与透明度
    • 拖拽结束后重排数据源,更新FlatList

简化示例:

import React, { useState, useRef } from 'react';
import { View, Text, StyleSheet, FlatList, LayoutAnimation, UIManager } from 'react-native';
import { GestureDetector, PanGesture } from 'react-native-gesture-handler';
import Animated, {
   useAnimatedStyle,
   useSharedValue,
   withSpring,
} from 'react-native-reanimated';

UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);

const initialData = Array.from({ length: 10 }).map((_, i) => ({ id: i, text: `Item ${i+1}` }));

export default function CustomDraggableList() {
   const [data, setData] = useState(initialData);
   const [draggedItem, setDraggedItem] = useState(null);
   const flatListRef = useRef(null);

   const handleDragStart = (item) => {
       setDraggedItem(item);
   };

   const handleDragEnd = (index) => {
       if (!draggedItem) return;
       const newData = [...data];
       const draggedIndex = newData.findIndex(i => i.id === draggedItem.id);
       newData.splice(draggedIndex, 1);
       newData.splice(index, 0, draggedItem);
       LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
       setData(newData);
       setDraggedItem(null);
   };

   const renderItem = ({ item, index }) => {
       const isDragged = draggedItem?.id === item.id;
       const translateY = useSharedValue(0);
       const panGesture = PanGesture()
           .onStart(() => {
               handleDragStart(item);
               translateY.value = 0;
           })
           .onUpdate((event) => {
               translateY.value = event.translationY;
           })
           .onEnd((event) => {
               translateY.value = withSpring(0);
               const newIndex = Math.max(0, Math.min(data.length - 1, index + Math.round(event.translationY / 80)));
               handleDragEnd(newIndex);
           });

       const animatedStyle = useAnimatedStyle(() => ({
           transform: [{ translateY: translateY.value }],
           opacity: isDragged ? 0.7 : 1,
       }));

       return (
           <GestureDetector gesture={panGesture}>
               <Animated.View style={[styles.item, animatedStyle]}>
                   <Text style={styles.text}>{item.text}</Text>
               </Animated.View>
           </GestureDetector>
       );
   };

   return (
       <FlatList
           ref={flatListRef}
           data={data}
           keyExtractor={(item) => item.id.toString()}
           renderItem={renderItem}
           contentContainerStyle={styles.listContainer}
       />
   );
}

const styles = StyleSheet.create({
   listContainer: {
       paddingVertical: 16,
   },
   item: {
       height: 80,
       marginHorizontal: 16,
       marginVertical: 8,
       backgroundColor: '#e0e0e0',
       borderRadius: 8,
       justifyContent: 'center',
       alignItems: 'center',
   },
   text: {
       fontSize: 18,
   },
});

说明:

  • 示例实现了基础拖拽排序,可根据需求扩展动画细节与交互逻辑
  • LayoutAnimation用于处理列表项重排时的过渡动画
  • 拖拽位置计算可进一步优化,比如根据绝对位置匹配对应列表项

注意事项

  • 使用第三方库时,务必查看最新文档确认与当前Expo SDK版本的兼容性
  • 手动实现需处理手势冲突、边界判断等细节,确保交互流畅
  • 测试时直接用Expo Go预览,若遇动画问题,检查react-native-reanimated配置(Expo环境下无需额外配置)

内容的提问来源于stack exchange,提问作者Felix Development

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 09:33:17