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

  • onClick: Triggered on click
  • onDoubleClick: Triggered on double-click
  • onLongPress: Triggered on long press
  • onMouseEnter: When mouse enters widget area
  • onMouseLeave: When mouse leaves widget area
  • onMouseMove: When mouse moves over widget
  • 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

  1. Choose appropriate renderer: Use Canvas renderer for complex interactions
  2. Event debouncing: Consider debouncing for frequent events (e.g., onMouseMove)
  3. Optimize state updates: Reduce unnecessary setState calls

Accessibility Considerations

When using GestureDetector, accessibility should be considered:

  1. Keyboard support: Make important interactions accessible via keyboard
  2. Focus indication: Visually distinguish focused elements
  3. 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.