前言
使用flutter開發(fā)是需要控件能拖動(dòng),比如畫板中的元素,或者工具條,搜索框,每個(gè)都單獨(dú)去實(shí)現(xiàn)拖動(dòng)還是比較麻煩的,將拖動(dòng)功能封裝成一個(gè)控件,需要的時(shí)候直接使用拖動(dòng)控件作為父控件這樣就方便很多了。
一、如何實(shí)現(xiàn)?
1、使用GestureDetector響應(yīng)拖動(dòng)事件
//總位移
var _unlimtedOffset = Offset.zero;
//當(dāng)前位移(有活動(dòng)區(qū)域限制時(shí),鼠標(biāo)超過(guò)邊界后當(dāng)前位移不等于總位移,此時(shí)總位移可以確?;氐竭吔鐑?nèi)鼠標(biāo)與控件的相對(duì)位置不變)
final _offset = ValueNotifier<Offset>(Offset.zero);
GestureDetector(
child: this.widget.child,
onPanUpdate: (detail) {
//累加拖動(dòng)距離
_unlimtedOffset += detail.delta;
}
)
2、使用Transform變換控件位置
使用translate變換位置即可
//ValueListenableBuilder監(jiān)聽_offset 改變,此處略
Transform.translate(
offset: offset,
child:GestureDetector()//上一步的child:GestureDetector
)
3、計(jì)算拖動(dòng)區(qū)域
這一步不是必須的,但是如果需要限制控件活動(dòng)范圍則需要這一步。
通過(guò)GlobalKey獲取控件大小,在GestureDetector的onPanUpdate事件中:
onPanUpdate: (detail) {
//拖動(dòng)區(qū)域?yàn)楦缚丶?,去掉則不受限制,但拖出父控件會(huì)被遮擋無(wú)法點(diǎn)擊。
//獲取父控件大小
RenderBox ? parentRenderBox = _mykey.currentContext
? .findAncestorRenderObjectOfType<RenderObject>() as RenderBox ? ;
final screenSize = parentRenderBox ? .size;
//獲取控件大小
final mySize = _mykey.currentContext ? .size;
final renderBox =
_mykey.currentContext ? .findRenderObject() as RenderBox ? ;
//獲取控件當(dāng)前位置
var originOffset = renderBox ? .localToGlobal(Offset.zero);
if (originOffset != null) {
originOffset = parentRenderBox ? .globalToLocal(originOffset);
}
if (screenSize == null || mySize == null || originOffset == null) {
return;
}
//計(jì)算不超出父控件區(qū)域
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,
);
}
//現(xiàn)在活動(dòng)區(qū)域?yàn)楦缚丶?--end
}
二、完整代碼
drag_move_box.dart
import 'package:flutter/material.dart';
/// 可拖動(dòng)容器
/// 拖動(dòng)范圍是父控件
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();
//當(dāng)前位移(有活動(dòng)區(qū)域限制時(shí),鼠標(biāo)超過(guò)邊界后當(dāng)前位移不等于總位移,此時(shí)總位移可以確?;氐竭吔鐑?nèi)鼠標(biāo)與控件的相對(duì)位置不變)
final _offset = ValueNotifier<Offset>(Offset.zero);
//總位移
var _unlimtedOffset = Offset.zero;
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: _offset,
builder:
//采用transform變換實(shí)現(xiàn)拖動(dòng)
(context, offset, widget) => Transform.translate(
key: _mykey,
offset: offset,
child: GestureDetector(
child: this.widget.child,
onPanUpdate: (detail) {
var off = _unlimtedOffset = _unlimtedOffset + detail.delta;
//拖動(dòng)區(qū)域?yàn)楦缚丶?,去掉則不受限制,但拖出父控件會(huì)被遮擋無(wú)法點(diǎn)擊。
//獲取父控件大小
RenderBox? parentRenderBox = _mykey.currentContext
?.findAncestorRenderObjectOfType<RenderObject>() as RenderBox?;
final screenSize = parentRenderBox?.size;
//獲取控件大小
final mySize = _mykey.currentContext?.size;
final renderBox =
_mykey.currentContext?.findRenderObject() as RenderBox?;
//獲取控件當(dāng)前位置
var originOffset = renderBox?.localToGlobal(Offset.zero);
if (originOffset != null) {
originOffset = parentRenderBox?.globalToLocal(originOffset);
}
if (screenSize == null || mySize == null || originOffset == null) {
return;
}
//計(jì)算不超出父控件區(qū)域
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,
);
}
//現(xiàn)在活動(dòng)區(qū)域?yàn)楦缚丶?--end
_offset.value = off;
},
),
),
);
}
}
三、使用示例
1、基本用法
DragMoveBox(
child:Text("You have pushed the button this many times:") //需要拖動(dòng)的控件
)
效果預(yù)覽文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-613025.html
總結(jié)
以上就是今天要講的內(nèi)容,本文提供了一種簡(jiǎn)單的拖動(dòng)控件實(shí)現(xiàn),尤其是封裝成容器后使用變得很簡(jiǎn)單,主要在于能想到translate變換可以改變位置,在了解通過(guò)GlobalKey獲取控件大小以及獲取控件大小的方法,很容易就實(shí)現(xiàn)拖動(dòng)功能了。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-613025.html
到了這里,關(guān)于Flutter 實(shí)現(xiàn)任意控件拖動(dòng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!