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,
})
属性
| 属性名 | 类型 | 说明 |
|---|---|---|
child | Widget | 正常状态下显示的子组件(必需) |
feedback | Widget | 拖拽过程中显示的反馈组件(必需) |
data | T | 拖拽操作传递的数据对象 |
axis | Axis | 限制拖拽方向(horizontal/vertical) |
childWhenDragging | Widget | 拖拽时在原位置显示的替代组件 |
feedbackOffset | Offset | 反馈组件相对于触摸点的偏移量 |
dragAnchor | DragAnchor | 拖拽锚点策略(child/pointer) |
maxSimultaneousDrags | int | 最大同时拖拽数量限制 |
onDragStarted | VoidCallback | 拖拽开始回调函数 |
onDragEnd | DragEndCallback | 拖拽结束回调函数 |
onDragUpdate | DragUpdateCallback | 拖拽位置更新回调 |
onDraggableCanceled | DraggableCanceledCallback | 拖拽取消回调 |
关键属性详解
feedback属性:
- 重要性: 必需属性,直接影响拖拽视觉效果
- 性能影响: 复杂的反馈 widget 会影响拖拽流畅度
- 最佳实践: 使用简化版本或缓存图像优化性能
data属性:
- 作用: 用于在
DragTarget之间传递信息 - 类型安全: 支持泛型,确保类型一致性
- 使用场景: 传递对象ID、数据内容等
axis属性:
- 限制效果: 可限制为水平或垂直方向拖拽
- 用户体验: 提供更精确的拖拽控制
- 适用场景: 列表重排序、滑块控制等
childWhenDragging属性:
- 视觉反馈: 提供拖拽时原位置的视觉状态
- 默认行为: 不设置时原位置保持原样
- 常用模式: 半透明、灰色显示或隐藏效果