LongPressDraggable

实现长按拖拽功能的组件

LongPressDraggable是Flutter中用于实现长按拖拽功能的控件,它允许用户通过长按某个widget来启动拖拽操作。该组件基于GestureDetector的长按手势识别,提供了完整的拖拽生命周期管理。

核心逻辑: 当用户长按目标widget时,会创建一个拖拽操作的"幽灵"副本(反馈widget),用户可以在屏幕上移动这个副本,松开手指时触发放置操作。

使用场景

  • 实现可重排序的列表项
  • 创建自定义的拖拽交互界面
  • 构建图形化编辑工具
  • 实现文件或内容的管理操作

示例

1. 基础拖拽

import 'package:flutter/material.dart';

class BasicDragExample extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('基础拖拽示例')),
      body: Center(
        child: LongPressDraggable<String>(
          data: '拖拽数据',
          feedback: Container(
            width: 100,
            height: 100,
            color: Colors.blue.withOpacity(0.7),
            child: Icon(Icons.drag_handle, color: Colors.white),
          ),
          child: Container(
            width: 100,
            height: 100,
            color: Colors.blue,
            child: Center(child: Text('长按拖拽', style: TextStyle(color: Colors.white))),
          ),
        ),
      ),
    );
  }
}

2. 与DragTarget配合使用

import 'package:flutter/material.dart';

class DragTargetExample extends StatefulWidget {
  
  _DragTargetExampleState createState() => _DragTargetExampleState();
}

class _DragTargetExampleState extends State<DragTargetExample> {
  List<String> items = ['Item 1', 'Item 2', 'Item 3'];
  List<String> targetItems = [];

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('拖拽目标示例')),
      body: Row(
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: items.length,
              itemBuilder: (context, index) => LongPressDraggable<String>(
                data: items[index],
                feedback: Material(
                  child: Container(
                    width: 200,
                    height: 50,
                    color: Colors.green.withOpacity(0.7),
                    child: Center(child: Text(items[index])),
                  ),
                ),
                child: ListTile(title: Text(items[index])),
              ),
            ),
          ),
          Expanded(
            child: DragTarget<String>(
              builder: (context, candidateData, rejectedData) => Container(
                color: Colors.grey[200],
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Icon(Icons.upload, size: 50),
                      Text('拖拽到这里', style: TextStyle(fontSize: 16)),
                      ...targetItems.map((item) => ListTile(title: Text(item))),
                    ],
                  ),
                ),
              ),
              onAccept: (data) {
                setState(() {
                  targetItems.add(data);
                  items.remove(data);
                });
              },
            ),
          ),
        ],
      ),
    );
  }
}

3. 自定义拖拽反馈和动画

import 'package:flutter/material.dart';

class CustomFeedbackExample extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('自定义反馈示例')),
      body: Center(
        child: LongPressDraggable<String>(
          data: '自定义拖拽',
          feedback: Transform.rotate(
            angle: 0.1, // 轻微旋转效果
            child: Material(
              elevation: 8,
              child: Container(
                width: 120,
                height: 120,
                decoration: BoxDecoration(
                  color: Colors.red,
                  borderRadius: BorderRadius.circular(12),
                ),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Icon(Icons.star, color: Colors.white, size: 40),
                    Text('拖拽中', style: TextStyle(color: Colors.white)),
                  ],
                ),
              ),
            ),
          ),
          childWhenDragging: Container(
            width: 100,
            height: 100,
            color: Colors.grey, // 拖拽时原位置显示灰色
          ),
          child: Container(
            width: 100,
            height: 100,
            decoration: BoxDecoration(
              color: Colors.red,
              borderRadius: BorderRadius.circular(8),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(Icons.star, color: Colors.white),
                Text('长按拖拽', style: TextStyle(color: Colors.white)),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

注意点

常见问题

  • 性能问题: 频繁创建复杂的反馈widget可能导致性能下降
  • 手势冲突: 与其他手势检测器(如InkWell)可能产生冲突
  • 边界处理: 拖拽超出屏幕边界时的行为需要特别处理

优化技巧

  • 为反馈widget使用缓存的图像或简化版本
  • 合理设置maxSimultaneousDrags限制同时拖拽数量
  • 使用childWhenDragging优化拖拽时的视觉反馈

最佳实践

// 最佳实践示例
LongPressDraggable<String>(
  data: itemData,
  feedback: _buildOptimizedFeedback(), // 使用优化后的反馈
  childWhenDragging: Opacity(
    opacity: 0.5, // 半透明效果更自然
    child: originalChild,
  ),
  onDragStarted: () => print('拖拽开始'),
  onDragEnd: (details) => print('拖拽结束'),
)

构造函数

LongPressDraggable<T>({
  Key? key,
  required Widget child,
  required Widget feedback,
  T? data,
  Axis? axis,
  Widget? childWhenDragging,
  Offset feedbackOffset = Offset.zero,
  DragAnchor dragAnchor = DragAnchor.child,
  int? maxSimultaneousDrags,
  VoidCallback? onDragStarted,
  DragEndCallback? onDragEnd,
  DragUpdateCallback? onDragUpdate,
  DraggableCanceledCallback? onDraggableCanceled,
})

属性

属性名类型说明
childWidget正常状态下显示的子组件(必需)
feedbackWidget拖拽过程中显示的反馈组件(必需)
dataT拖拽操作传递的数据对象
axisAxis限制拖拽方向(horizontal/vertical)
childWhenDraggingWidget拖拽时在原位置显示的替代组件
feedbackOffsetOffset反馈组件相对于触摸点的偏移量
dragAnchorDragAnchor拖拽锚点策略(child/pointer)
maxSimultaneousDragsint最大同时拖拽数量限制
onDragStartedVoidCallback拖拽开始回调函数
onDragEndDragEndCallback拖拽结束回调函数
onDragUpdateDragUpdateCallback拖拽位置更新回调
onDraggableCanceledDraggableCanceledCallback拖拽取消回调

关键属性详解

feedback属性:

  • 重要性: 必需属性,直接影响拖拽视觉效果
  • 性能影响: 复杂的反馈 widget 会影响拖拽流畅度
  • 最佳实践: 使用简化版本或缓存图像优化性能

data属性:

  • 作用: 用于在DragTarget之间传递信息
  • 类型安全: 支持泛型,确保类型一致性
  • 使用场景: 传递对象ID、数据内容等

axis属性:

  • 限制效果: 可限制为水平或垂直方向拖拽
  • 用户体验: 提供更精确的拖拽控制
  • 适用场景: 列表重排序、滑块控制等

childWhenDragging属性:

  • 视觉反馈: 提供拖拽时原位置的视觉状态
  • 默认行为: 不设置时原位置保持原样
  • 常用模式: 半透明、灰色显示或隐藏效果