Draggable
Draggable
은 하위 위젯을 마우스로 드래그할 수 있게 만드는 위젯입니다. 내부적으로 Transform
과 GestureDetector
를 사용하여 드래그 기능을 구현합니다.
개요
Draggable 위젯은 사용자 인터랙션을 통해 위젯을 화면상에서 이동할 수 있게 합니다. 드래그 시작, 진행, 종료 이벤트를 제공하여 세밀한 제어가 가능합니다.
언제 사용하나요?
- 드래그 앤 드롭 인터페이스: 파일, 카드, 아이템을 이동할 때
- 대화형 다이어그램: 노드나 요소를 자유롭게 배치할 때
- 위젯 재배치: 사용자가 UI 요소의 위치를 커스터마이징할 때
- 게임 요소: 퍼즐 조각이나 게임 오브젝트를 움직일 때
- 시각적 편집기: 디자인 툴에서 요소를 배치할 때
기본 사용법
import { Draggable, Container } from '@meursyphus/flitter';
Draggable({
onDragUpdate: ({ delta, movement }) => {
console.log('드래그 중:', { delta, movement });
},
onDragStart: () => {
console.log('드래그 시작');
},
onDragEnd: () => {
console.log('드래그 종료');
},
child: Container({
width: 100,
height: 100,
color: 'blue'
})
})
Props
속성 | 타입 | 설명 |
---|---|---|
child | Widget? | 드래그 가능하게 만들 하위 위젯 |
onDragUpdate | (event: { delta: Offset, movement: Offset }) => void? | 드래그 중 호출되는 콜백 |
onDragStart | () => void? | 드래그 시작 시 호출되는 콜백 |
onDragEnd | () => void? | 드래그 종료 시 호출되는 콜백 |
feedback | Widget? | 드래그 중 표시할 피드백 위젯 |
이벤트 객체
delta
: 이전 프레임 대비 이동량movement
: 드래그 시작점 대비 총 이동량
실제 사용 예제
1. 기본 드래그 박스
import { Draggable, Container, Text } from '@meursyphus/flitter';
Draggable({
onDragUpdate: ({ movement }) => {
console.log(`이동: x=${movement.x}, y=${movement.y}`);
},
child: Container({
width: 120,
height: 80,
color: '#3498db',
child: Text('드래그하세요', {
style: { color: 'white', textAlign: 'center' }
})
})
})
2. 상태 관리와 함께 사용
import { StatefulWidget, State, Draggable, Container, Transform } from '@meursyphus/flitter';
class DraggableBox extends StatefulWidget {
createState() {
return new DraggableBoxState();
}
}
class DraggableBoxState extends State<DraggableBox> {
position = { x: 0, y: 0 };
isDragging = false;
handleDragStart = () => {
this.setState(() => {
this.isDragging = true;
});
}
handleDragUpdate = ({ movement }) => {
this.setState(() => {
this.position = { x: movement.x, y: movement.y };
});
}
handleDragEnd = () => {
this.setState(() => {
this.isDragging = false;
});
}
build() {
return Transform.translate({
offset: this.position,
child: Draggable({
onDragStart: this.handleDragStart,
onDragUpdate: this.handleDragUpdate,
onDragEnd: this.handleDragEnd,
child: Container({
width: 100,
height: 100,
color: this.isDragging ? '#e74c3c' : '#2ecc71'
})
})
});
}
}
3. 드래그 영역 제한
import { Draggable, Container } from '@meursyphus/flitter';
class BoundedDraggable extends StatefulWidget {
createState() {
return new BoundedDraggableState();
}
}
class BoundedDraggableState extends State<BoundedDraggable> {
position = { x: 0, y: 0 };
bounds = { minX: -100, maxX: 100, minY: -50, maxY: 50 };
handleDragUpdate = ({ movement }) => {
const clampedX = Math.max(this.bounds.minX,
Math.min(this.bounds.maxX, movement.x));
const clampedY = Math.max(this.bounds.minY,
Math.min(this.bounds.maxY, movement.y));
this.setState(() => {
this.position = { x: clampedX, y: clampedY };
});
}
build() {
return Transform.translate({
offset: this.position,
child: Draggable({
onDragUpdate: this.handleDragUpdate,
child: Container({
width: 60,
height: 60,
color: '#9b59b6'
})
})
});
}
}
4. 드래그 피드백 위젯
import { Draggable, Container, Text, Opacity } from '@meursyphus/flitter';
Draggable({
child: Container({
width: 100,
height: 100,
color: '#1abc9c',
child: Text('원본')
}),
feedback: Opacity({
opacity: 0.7,
child: Container({
width: 100,
height: 100,
color: '#16a085',
child: Text('드래그 중', {
style: { color: 'white' }
})
})
})
})
5. 다중 드래그 객체
import { Column, Draggable, Container, Text, EdgeInsets } from '@meursyphus/flitter';
const colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12'];
const items = colors.map((color, index) =>
Draggable({
onDragUpdate: ({ movement }) => {
console.log(`Item ${index + 1} 이동:`, movement);
},
child: Container({
width: 80,
height: 60,
color,
margin: EdgeInsets.all(5),
child: Text(`${index + 1}`, {
style: { color: 'white', textAlign: 'center' }
})
})
})
);
Column({
children: items
})
6. 드래그 상태 시각화
import { Draggable, Container, Text, Column } from '@meursyphus/flitter';
class DragVisualizer extends StatefulWidget {
createState() {
return new DragVisualizerState();
}
}
class DragVisualizerState extends State<DragVisualizer> {
status = '대기 중';
position = { x: 0, y: 0 };
build() {
return Column({
children: [
Text(`상태: ${this.status}`),
Text(`위치: (${this.position.x.toFixed(1)}, ${this.position.y.toFixed(1)})`),
Draggable({
onDragStart: () => {
this.setState(() => {
this.status = '드래그 중';
});
},
onDragUpdate: ({ movement }) => {
this.setState(() => {
this.position = movement;
});
},
onDragEnd: () => {
this.setState(() => {
this.status = '완료';
});
},
child: Container({
width: 100,
height: 100,
color: '#8e44ad'
})
})
]
});
}
}
주의사항
-
성능: 드래그 이벤트는 빈번하게 발생하므로
onDragUpdate
에서 무거운 연산을 피하세요. -
상태 업데이트: 드래그 중 상태 변경 시 적절한
setState
호출이 필요합니다. -
좌표계:
movement
는 드래그 시작점 기준이고,delta
는 이전 프레임 기준입니다. -
Transform과의 관계: Draggable은 내부적으로 Transform을 사용하므로 추가 Transform 적용 시 주의가 필요합니다.
-
드롭 타겟: 현재 Draggable은 드래그만 지원하며, 드롭 타겟 기능은 별도로 구현해야 합니다.
관련 위젯
- Transform: 위젯 변형 및 위치 조정
- GestureDetector: 마우스/터치 이벤트 감지
- Container: 드래그 가능한 영역 정의
- ZIndex: 드래그 중 레이어 순서 조정