Basic Interactions - GestureDetector
User interactions are essential in web applications. Flitter supports simple handling of various user inputs through the GestureDetector
widget.
What is GestureDetector?
GestureDetector
is a widget that detects various gestures on its child widget.
It can handle various user inputs such as clicks, double-clicks, hovers, drags, and more.
Basic Usage
GestureDetector({
onClick: () => {
console.log('Clicked!');
},
child: Container({
width: 100,
height: 100,
color: '#3B82F6',
child: Center({
child: Text('Click me')
})
})
})
Practical Examples
Using GestureDetector
Implement various interactions like click counters, hover effects, drag and drop, and more.
Click Counter
GestureDetector({
onClick: () => {
this.setState(() => {
this.count++;
});
},
child: Container({
// 클릭할 수 있는 영역
})
})
Hover Effect
GestureDetector({
onMouseEnter: () => {
this.setState(() => {
this.isHovered = true;
});
},
onMouseLeave: () => {
this.setState(() => {
this.isHovered = false;
});
},
child: Container({
color: this.isHovered ? '#10B981' : '#6B7280'
})
})
Drag and Drop
🎆 Draggable 위젯 사용 (추천)
// 1. import에서 Draggable 가져오기
import { Draggable } from '@meursyphus/flitter';
// 2. 간단한 사용법
Draggable({
onDragUpdate: ({ delta }) => {
this.setState(() => {
this.position = {
x: delta.x, // 전체 이동 거리
y: delta.y
};
});
},
child: Container({
width: 60,
height: 60,
decoration: new BoxDecoration({
color: '#8B5CF6',
borderRadius: BorderRadius.circular(30)
})
})
})
🔧 Draggable의 내부 구현 (참고용)
// Draggable은 내부적으로 이렇게 구현되어 있음
build(context: BuildContext): Widget {
return Transform.translate({
offset: this.delta, // 자동 위치 계산
child: GestureDetector({
onDragStart: this.handleMouseDown,
onDragMove: this.handleMouseMove,
onDragEnd: this.handleMouseUp,
child: this.widget.feedback
})
});
}
// GestureDetector를 사용하여 구현
✨ Draggable의 장점
- • Transform 자동 처리: 위치 계산과 이동을 내부에서 처리
- • 상태 관리: origin, delta, lastDelta 등을 자동 관리
- • Flutter 호환: Flutter의 Draggable과 동일한 API
- • 더 적은 코드: 복잡한 로직을 위젯이 대신 처리
🎯 Gesture Types
마우스 이벤트:
- • onClick: 클릭 시
- • onDoubleClick: 더블클릭 시
- • onMouseEnter: 마우스 진입 시
- • onMouseLeave: 마우스 벗어날 시
- • onMouseMove: 마우스 이동 시
터치/드래그 이벤트:
- • onPanStart: 드래그 시작
- • onPanUpdate: 드래그 중
- • onPanEnd: 드래그 종료
- • onLongPress: 길게 누르기
- • onTap: 탭 (모바일)
💡 Try it out!
- • GestureDetector는 StatefulWidget과 함께 사용하여 상태를 관리합니다
- • 이벤트 핸들러 안에서는 반드시 setState를 사용하여 상태를 업데이트합니다
- • Canvas 렌더러에서 더 나은 성능을 제공합니다
- • 여러 GestureDetector를 중첩할 수 있습니다
Using with StatefulWidget
Most interactions involve state changes, so they are used together with StatefulWidget
:
class InteractiveButton extends StatefulWidget {
createState() {
return new InteractiveButtonState();
}
}
class InteractiveButtonState extends State<InteractiveButton> {
isPressed = false;
isHovered = false;
build(context: BuildContext) {
return GestureDetector({
onClick: () => {
console.log('Button clicked!');
},
onMouseEnter: () => {
this.setState(() => {
this.isHovered = true;
});
},
onMouseLeave: () => {
this.setState(() => {
this.isHovered = false;
});
},
onPanStart: () => {
this.setState(() => {
this.isPressed = true;
});
},
onPanEnd: () => {
this.setState(() => {
this.isPressed = false;
});
},
child: Container({
padding: EdgeInsets.symmetric({ horizontal: 24, vertical: 12 }),
decoration: new BoxDecoration({
color: this.isPressed
? '#1E40AF'
: this.isHovered
? '#2563EB'
: '#3B82F6',
borderRadius: BorderRadius.circular(8)
}),
child: Text('Interactive Button', {
style: new TextStyle({
color: '#FFFFFF',
fontWeight: 'bold'
})
})
})
});
}
}
Detailed Event Types
Click-related Events
- onClick: Triggered on click
- onDoubleClick: Triggered on double-click
- onLongPress: Triggered on long press
Mouse-related Events
- onMouseEnter: When mouse enters widget area
- onMouseLeave: When mouse leaves widget area
- onMouseMove: When mouse moves over widget
Drag-related Events
- onPanStart: When drag starts
- onPanUpdate: During drag (provides delta value)
- onPanEnd: When drag ends
Practical Usage Examples
1. Toggle Switch
class ToggleSwitch extends StatefulWidget {
createState() {
return new ToggleSwitchState();
}
}
class ToggleSwitchState extends State<ToggleSwitch> {
isOn = false;
build(context: BuildContext) {
return GestureDetector({
onClick: () => {
this.setState(() => {
this.isOn = !this.isOn;
});
},
child: Container({
width: 60,
height: 30,
decoration: new BoxDecoration({
color: this.isOn ? '#10B981' : '#6B7280',
borderRadius: BorderRadius.circular(15)
}),
child: Stack({
children: [
AnimatedPositioned({
duration: 200,
left: this.isOn ? 30 : 0,
child: Container({
width: 30,
height: 30,
decoration: new BoxDecoration({
color: '#FFFFFF',
borderRadius: BorderRadius.circular(15)
})
})
})
]
})
})
});
}
}
2. Dropdown Menu
class DropdownMenu extends StatefulWidget {
createState() {
return new DropdownMenuState();
}
}
class DropdownMenuState extends State<DropdownMenu> {
isOpen = false;
build(context: BuildContext) {
return Column({
children: [
GestureDetector({
onClick: () => {
this.setState(() => {
this.isOpen = !this.isOpen;
});
},
child: Container({
padding: EdgeInsets.all(12),
decoration: new BoxDecoration({
color: '#374151',
borderRadius: BorderRadius.circular(6)
}),
child: Row({
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Select Menu', {
style: new TextStyle({ color: '#E5E7EB' })
}),
Icon(this.isOpen ? 'arrow_up' : 'arrow_down')
]
})
})
}),
if (this.isOpen) Container({
margin: EdgeInsets.only({ top: 4 }),
decoration: new BoxDecoration({
color: '#374151',
borderRadius: BorderRadius.circular(6)
}),
child: Column({
children: ['Option 1', 'Option 2', 'Option 3'].map(option =>
GestureDetector({
onClick: () => {
console.log(`Selected: ${option}`);
this.setState(() => {
this.isOpen = false;
});
},
child: Container({
padding: EdgeInsets.all(12),
child: Text(option, {
style: new TextStyle({ color: '#E5E7EB' })
})
})
})
)
})
})
]
});
}
}
Performance Optimization Tips
- Choose appropriate renderer: Use Canvas renderer for complex interactions
- Event debouncing: Consider debouncing for frequent events (e.g., onMouseMove)
- Optimize state updates: Reduce unnecessary setState calls
Accessibility Considerations
When using GestureDetector, accessibility should be considered:
- Keyboard support: Make important interactions accessible via keyboard
- Focus indication: Visually distinguish focused elements
- Sufficient touch area: Ensure minimum 44x44px size for mobile
Next Steps
You’ve now learned all the basics of Flitter! Learn more in-depth content in Core Concepts, or explore various widgets in the Widget Reference.
Summary
- GestureDetector: Core widget for handling user input
- Used with StatefulWidget: Essential for state changes
- Various events: Supports clicks, hovers, drags, and more
- Use setState: Update state within event handlers
- Practical applications: Can implement buttons, toggles, dropdowns, etc.