🚚
【Flutter】Draggableでドラッグ中のオブジェクトを範囲内で制御したい
経緯
Draggable Widgetをドラッグすると自由にWindow内を動かせるが、特定の範囲外(下記緑範囲)に出ないように制御したかった。
※今回の手法以外に良い方法があれば、コメントでご教授頂きたいです。
Draggableの挙動
赤色のブロックを緑色の範囲内で動かしたいが、範囲を出てしまう。
赤色ブロック
final double _moveBlockSize = 100;
Draggable(
childWhenDragging: Container(),
feedback: Container(
color: Colors.red,
height: _moveBlockSize,
width: _moveBlockSize0,
),
child: Container(
color: Colors.red,
height: _moveBlockSize,
width: _moveBlockSize,
),
),
※childWhenDraggingはドラッグ中のchildの状態
Draggable Widget と Show Widgetに分ける
Draggable Widgetは透過し、表示するのはShow Widgetとする。
Draggable Widgetの位置情報をonDragUpdateで取得し、それをShow Widgetの位置とすることで擬似的にDraggable Widgetを動かす。
これで赤色のブロックの位置を数値で扱える。
位置情報を取得、更新
Offset _moveBlockOffset = Offset.zero;// 赤色ブロックの位置
Stack(
children: [
// Show Widget
Positioned(
left: _moveBlockOffset.dx,
top: _moveBlockOffset.dy,
child: Container(
color: Colors.red,
height: _moveBlockSize,
width: _moveBlockSize,
),
),
// Draggable Widget
Positioned(
left: _moveBlockOffset.dx,
top: _moveBlockOffset.dy,
child: Draggable(
onDragUpdate: (details) {
setState(() {
_moveBlockOffset = Offset(
_moveBlockOffset.dx + details.delta.dx,
_moveBlockOffset.dy + details.delta.dy,
);
});
},
childWhenDragging: Container(),
feedback: Container(),
child: Container(
color: Colors.transparent,// draggableは透明にする
height: _moveBlockSize,
width: _moveBlockSize,
),
),
),
],
),
位置情報に制約を加える
あとは、位置情報(数値)を特定の範囲内とするように制限する。
表示されているダミーはこの範囲内を動く。
※clamp(5, 10)は5~10の値に制限する。3->5, 15->10
制約メソッド
final Size _moveRange = const Size(500, 500);// 移動範囲
void clampMoveBlockOffset() {
double clampedX = _moveBlockOffset.dx.clamp(0, _moveRange.width - _moveBlockSize);
double clampedY = _moveBlockOffset.dy.clamp(0, _moveRange.height - _moveBlockSize);
_moveBlockOffset = Offset(clampedX, clampedY);
}
コードサンプル
コード全文
import 'package:flutter/material.dart';
class DraggableRange extends StatefulWidget {
const DraggableRange({
super.key,
});
State<DraggableRange> createState() => _DraggableRangeState();
}
class _DraggableRangeState extends State<DraggableRange> {
final double _moveBlockSize = 100;
final Size _moveRange = const Size(500, 500);
Offset _moveBlockOffset = Offset.zero;
void clampMoveBlockOffset() {
double clampedX = _moveBlockOffset.dx.clamp(0, _moveRange.width - _moveBlockSize);
double clampedY = _moveBlockOffset.dy.clamp(0, _moveRange.height - _moveBlockSize);
_moveBlockOffset = Offset(clampedX, clampedY);
}
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: _moveRange.width,
height: _moveRange.height,
color: Colors.green,
margin: const EdgeInsets.only(top: 100, left: 100),
child: Stack(
children: [
Positioned(
left: _moveBlockOffset.dx,
top: _moveBlockOffset.dy,
child: Container(
color: Colors.red,
height: _moveBlockSize,
width: _moveBlockSize,
),
),
Positioned(
left: _moveBlockOffset.dx,
top: _moveBlockOffset.dy,
child: Draggable(
onDragUpdate: (details) {
setState(() {
_moveBlockOffset = Offset(
_moveBlockOffset.dx + details.delta.dx,
_moveBlockOffset.dy + details.delta.dy,
);
clampMoveBlockOffset();
});
},
childWhenDragging: Container(),
feedback: Container(),
child: Container(
color: Colors.transparent,
height: _moveBlockSize,
width: _moveBlockSize,
),
),
),
],
),
),
);
}
}
Discussion