flutter 拖拽
本文最后更新于:2022年4月22日 上午
flutter 拖拽
拖拽
ReorderableListView, 所有的子 widget
都需要key
,长按可进行拖拽
body: const TestReorderable()
class TestReorderable extends StatefulWidget {
const TestReorderable({Key? key}) : super(key: key);
@override
State<TestReorderable> createState() => _TestReorderableState();
}
class _TestReorderableState extends State<TestReorderable> {
final boxes = [
Box(color: Colors.blue[300]!, key: UniqueKey()),
Box(color: Colors.blue[600]!, key: UniqueKey()),
Box(color: Colors.blue[900]!, key: UniqueKey()),
];
@override
Widget build(BuildContext context) {
return ReorderableListView(
children: boxes,
// 拖拽后触发的回调
onReorder: (oldIndex, newIndex) {
final widget = boxes.removeAt(oldIndex);
if (newIndex > oldIndex) newIndex--; // 往后拖需要手动减
boxes.insert(newIndex, widget);
setState(() {});
},
);
}
}
class Box extends StatelessWidget {
final Color color;
const Box({Key? key, required this.color}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(child: Container(color: color, width: 50, height: 50, margin: const EdgeInsets.all(8)));
}
}
手动实现拖拽
Draggable
可以把需要拖拽的子组件包裹住
Draggable(
child:
feedback: 拖拽时 手指的widget,
childWhenDragging: 拖拽时 留在原地的widget
)
定位动画
AnimatedPositioned
AnimatedPositioned()
Listerner 监听
监听move
事件
封装自定义拖拽
使用
final color = [
Colors.blue[300]!,
Colors.green[600]!,
Colors.blue[600]!,
Colors.blue[900]!,
Colors.green[200]!,
];
Drag<Color>(
childSize: const Size(76, 76),
direction: Axis.vertical,
list: color,
buildItem: (index) => Container(margin: const EdgeInsets.all(8), color: color[index], width: 60, height: 60),
buildKey: (index) => ValueKey(color[index]),
);
import 'package:flutter/material.dart';
enum _MoveActions {
PREV, // 前一个
NEXT // 后一个
}
class Drag<T> extends StatefulWidget {
// final int itemCount;
final List<T> list;
final Widget Function(int) buildItem;
final Key Function(int) buildKey;
final Axis direction; // 拖拽方向
final Size childSize; // 子元素尺寸
const Drag({
Key? key,
required this.buildItem,
required this.buildKey,
required this.list,
this.direction = Axis.horizontal,
required this.childSize,
}) : super(key: key);
@override
State<Drag> createState() => _DragState();
}
class _DragState extends State<Drag> {
int? currentIndex; // 当前拖动的widget
handleMove(PointerMoveEvent event) {
// 垂直方向滚动需要 减去顶部 tabbar 高度,目前先写死
final offset = widget.direction == Axis.horizontal ? event.position.dx : event.position.dy - 60;
final targetOffset = (widget.direction == Axis.horizontal ? widget.childSize.width : widget.childSize.height);
// 向后移动
if (offset > (currentIndex! + 1) * targetOffset) {
if (currentIndex! == widget.list.length - 1) return;
_move(_MoveActions.NEXT);
// 向前移动
} else if (offset < currentIndex! * targetOffset) {
if (currentIndex! == 0) return;
_move(_MoveActions.PREV);
}
}
// 移动
_move(_MoveActions _moveActions) {
setState(() {
final currentItem = widget.list[currentIndex!];
// 判断向前移动还是向后移动
final targetIndex = _moveActions == _MoveActions.PREV ? currentIndex! - 1 : currentIndex! + 1;
widget.list[currentIndex!] = widget.list[targetIndex];
widget.list[targetIndex] = currentItem;
// 更新当前索引
currentIndex = _moveActions == _MoveActions.PREV ? currentIndex! - 1 : currentIndex! + 1;
});
}
@override
Widget build(BuildContext context) {
return Listener(
onPointerMove: handleMove,
child: Stack(
children: List.generate(
widget.list.length,
(index) => _DragItem(
key: widget.buildKey(index),
child: widget.buildItem(index),
x: widget.direction == Axis.horizontal ? index * widget.childSize.width : 0,
y: widget.direction == Axis.horizontal ? 0 : index * widget.childSize.height,
onDragStarted: () => setState(() => currentIndex = index),
childSize: widget.childSize,
),
)),
);
}
}
class _DragItem extends StatelessWidget {
final Widget child; // 需要展示的内容
final double x; // x 轴
final double y; // y 轴
void Function()? onDragStarted; // 开始拖拽事件
final Size childSize; // 子元素尺寸
_DragItem({
Key? key,
required this.x,
required this.y,
required this.child,
required this.onDragStarted,
required this.childSize,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return AnimatedPositioned(
duration: const Duration(milliseconds: 300), // 动画特效
left: x,
top: y,
child: Draggable(
onDragStarted: onDragStarted,
childWhenDragging: Container(width: childSize.width, height: childSize.height, color: Colors.transparent),
feedback: Container(width: childSize.width, height: childSize.height, color: Colors.transparent, child: child),
child: Container(width: childSize.width, height: childSize.height, color: Colors.transparent, child: child),
),
);
}
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处。