Каталог статей
предисловие
Использование флаттер-разработки требует элементов управления, которые можно перетаскивать, таких как элементы на чертежной доске или панели инструментов и поля поиска.Осуществить перетаскивание для каждого в отдельности довольно проблематично.Функция перетаскивания инкапсулирована в элемент управления, который можно использовать напрямую когда это необходимо.Гораздо удобнее перетаскивать элемент управления в качестве родительского элемента управления.
1. Как этого добиться?
1. Используйте GestureDetector для реагирования на события перетаскивания
//总位移
var _unlimtedOffset = Offset.zero;
//当前位移(有活动区域限制时,鼠标超过边界后当前位移不等于总位移,此时总位移可以确保回到边界内鼠标与控件的相对位置不变)
final _offset = ValueNotifier<Offset>(Offset.zero);
GestureDetector(
child: this.widget.child,
onPanUpdate: (detail) {
//累加拖动距离
_unlimtedOffset += detail.delta;
}
)
2. Используйте Transform, чтобы преобразовать положение элемента управления.
Используйте перевод, чтобы изменить положение
//ValueListenableBuilder监听_offset 改变,此处略
Transform.translate(
offset: offset,
child:GestureDetector()//上一步的child:GestureDetector
)
3. Рассчитайте площадь перетаскивания
Этот шаг необязателен, но необходим, если вам нужно ограничить диапазон контрольных действий.
Получите размер элемента управления через GlobalKey в событии onPanUpdate GestureDetector:
onPanUpdate: (detail) {
//拖动区域为父控件,去掉则不受限制,但拖出父控件会被遮挡无法点击。
//获取父控件大小
RenderBox ? parentRenderBox = _mykey.currentContext
? .findAncestorRenderObjectOfType<RenderObject>() as RenderBox ? ;
final screenSize = parentRenderBox ? .size;
//获取控件大小
final mySize = _mykey.currentContext ? .size;
final renderBox =
_mykey.currentContext ? .findRenderObject() as RenderBox ? ;
//获取控件当前位置
var originOffset = renderBox ? .localToGlobal(Offset.zero);
if (originOffset != null) {
originOffset = parentRenderBox ? .globalToLocal(originOffset);
}
if (screenSize == null || mySize == null || originOffset == null) {
return;
}
//计算不超出父控件区域
if (off.dx < -originOffset.dx) {
off = Offset(-originOffset.dx, off.dy);
}
else if (off.dx >
screenSize.width - mySize.width - originOffset.dx) {
off = Offset(
screenSize.width - mySize.width - originOffset.dx,
off.dy,
);
}
if (off.dy < -originOffset.dy) {
off = Offset(off.dx, -originOffset.dy);
}
else if (off.dy >
screenSize.height - mySize.height - originOffset.dy) {
off = Offset(
off.dx,
screenSize.height - mySize.height - originOffset.dy,
);
}
//现在活动区域为父控件 --end
}
2. Полный код
drag_move_box.dart
import 'package:flutter/material.dart';
/// 可拖动容器
/// 拖动范围是父控件
class DragMoveBox extends StatefulWidget {
final Widget child;
const DragMoveBox({
super.key,
required this.child,
});
State<DragMoveBox> createState() => _DragMoveBoxState();
}
class _DragMoveBoxState extends State<DragMoveBox> {
final GlobalKey _mykey = GlobalKey();
//当前位移(有活动区域限制时,鼠标超过边界后当前位移不等于总位移,此时总位移可以确保回到边界内鼠标与控件的相对位置不变)
final _offset = ValueNotifier<Offset>(Offset.zero);
//总位移
var _unlimtedOffset = Offset.zero;
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: _offset,
builder:
//采用transform变换实现拖动
(context, offset, widget) => Transform.translate(
key: _mykey,
offset: offset,
child: GestureDetector(
child: this.widget.child,
onPanUpdate: (detail) {
var off = _unlimtedOffset = _unlimtedOffset + detail.delta;
//拖动区域为父控件,去掉则不受限制,但拖出父控件会被遮挡无法点击。
//获取父控件大小
RenderBox? parentRenderBox = _mykey.currentContext
?.findAncestorRenderObjectOfType<RenderObject>() as RenderBox?;
final screenSize = parentRenderBox?.size;
//获取控件大小
final mySize = _mykey.currentContext?.size;
final renderBox =
_mykey.currentContext?.findRenderObject() as RenderBox?;
//获取控件当前位置
var originOffset = renderBox?.localToGlobal(Offset.zero);
if (originOffset != null) {
originOffset = parentRenderBox?.globalToLocal(originOffset);
}
if (screenSize == null || mySize == null || originOffset == null) {
return;
}
//计算不超出父控件区域
if (off.dx < -originOffset.dx) {
off = Offset(-originOffset.dx, off.dy);
} else if (off.dx >
screenSize.width - mySize.width - originOffset.dx) {
off = Offset(
screenSize.width - mySize.width - originOffset.dx,
off.dy,
);
}
if (off.dy < -originOffset.dy) {
off = Offset(off.dx, -originOffset.dy);
} else if (off.dy >
screenSize.height - mySize.height - originOffset.dy) {
off = Offset(
off.dx,
screenSize.height - mySize.height - originOffset.dy,
);
}
//现在活动区域为父控件 --end
_offset.value = off;
},
),
),
);
}
}
3. Пример использования
1. Основное использование
DragMoveBox(
child:Text("You have pushed the button this many times:") //需要拖动的控件
)
Предварительный просмотр эффекта
Подведем итог
Это то, о чем я хочу поговорить сегодня. В этой статье представлена простая реализация управления перетаскиванием, особенно после того, как она упакована в контейнер, она становится очень простой в использовании. Основная причина в том, что вы можете придумать преобразование перевода для изменения После понимания размера элемента управления с помощью GlobalKey и метода получения размера элемента управления можно легко реализовать функцию перетаскивания.