티스토리 뷰

Flutter 앱 개발에서 사용자와의 상호작용은 매우 중요한 요소입니다. 특히 화면의 특정 요소를 터치하거나 드래그할 때 이를 적절하게 처리하는 방법은 앱의 사용자 경험을 크게 향상시킵니다. 이번 글에서는 GestureDetector 위젯을 사용해 Flutter에서 터치 이벤트를 쉽게 구현하는 방법을 알아보겠습니다.

참고. Handle taps

GestureDetector란?

GestureDetector는 Flutter에서 터치와 같은 다양한 제스처를 감지하는 위젯입니다. 이를 사용하면 사용자가 화면을 터치하거나 드래그하는 등의 동작에 따라 원하는 동작을 수행할 수 있습니다. 예를 들어, 버튼을 터치했을 때 스낵바를 표시하는 기능을 GestureDetector를 통해 쉽게 구현할 수 있습니다.

Flutter의 주요 버튼 위젯들

Flutter에는 GestureDetector 없이도 사용할 수 있는 기본 버튼들이 존재합니다:

  • ElevatedButton: 일반적으로 배경색이 있는 입체 버튼입니다.
  • TextButton: 텍스트만 있는 버튼으로, 가벼운 터치 기능에 적합합니다.
  • CupertinoButton: iOS 스타일의 버튼으로, iOS와 유사한 UI를 구현할 때 유용합니다.

하지만 이러한 기본 버튼들 외에도 다양한 상호작용을 세밀하게 제어하려면 GestureDetector를 사용하는 것이 좋습니다.

GestureDetector로 사용자 터치 이벤트 처리하기

1. 버튼 생성하기

Flutter에서 버튼처럼 동작할 수 있는 Container 위젯을 만들고, GestureDetector로 이를 감쌉니다.

Container(
  padding: const EdgeInsets.all(12),
  decoration: BoxDecoration(
    color: Colors.lightBlue,
    borderRadius: BorderRadius.circular(8),
  ),
  child: const Text('My Button'),
);

2. GestureDetector 추가하여 터치 이벤트 감지

이제 GestureDetector를 사용하여 터치 이벤트를 감지하고, 사용자가 버튼을 터치했을 때 스낵바를 표시해보겠습니다.

GestureDetector(
  onTap: () {
    const snackBar = SnackBar(content: Text('Tap'));
    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  },
  child: Container(
    padding: const EdgeInsets.all(12),
    decoration: BoxDecoration(
      color: Colors.lightBlue,
      borderRadius: BorderRadius.circular(8),
    ),
    child: const Text('My Button'),
  ),
)

위의 코드에서 onTap 콜백은 사용자가 버튼을 터치했을 때 실행됩니다. ScaffoldMessenger.of(context).showSnackBar를 통해 간단한 스낵바 메시지를 표시할 수 있습니다.

GestureDetector 활용 시 주의할 점

  • 터치 영역 확장: GestureDetector의 터치 영역은 해당 위젯의 크기에 따라 결정됩니다. 터치 영역을 넓게 설정하고 싶다면, Container의 패딩이나 크기를 조정해 보세요.
  • Material Ripple 효과 추가: Flutter에서 기본 제공하는 버튼은 터치할 때 리플 효과가 나타나 사용자가 터치를 감지하기 쉽습니다. 커스텀 버튼에도 동일한 효과를 추가하려면 InkWell 또는 InkResponse를 사용하여 터치 리플 효과를 적용할 수 있습니다.
InkWell(
  onTap: () {
    // 터치 시 실행할 작업
  },
  child: Container(
    padding: const EdgeInsets.all(12),
    decoration: BoxDecoration(
      color: Colors.lightBlue,
      borderRadius: BorderRadius.circular(8),
    ),
    child: const Text('My Button'),
  ),
)

Material Touch Ripple 효과 추가

터치 시 리플 효과를 통해 터치 반응을 더욱 직관적으로 전달할 수 있습니다. InkWell이나 InkResponse 위젯을 사용하면 손쉽게 리플 효과를 적용할 수 있습니다.

GestureDetector의 다양한 사용법

Flutter에서 터치 이벤트를 감지하는 방법에는 onTap 외에도 다양한 제스처 콜백이 존재합니다.

  1. onTap: 터치 시 실행됩니다.
  2. onDoubleTap: 두 번 연속 터치 시 실행됩니다.
  3. onLongPress: 길게 누를 때 실행됩니다.
  4. onHorizontalDrag: 수평으로 드래그할 때 실행됩니다.
  5. onVerticalDrag: 수직으로 드래그할 때 실행됩니다.

이러한 제스처 감지 기능을 적절히 활용하면 더욱 직관적이고 풍부한 상호작용을 제공하는 UI를 만들 수 있습니다.

예시: 드래그 감지하기

GestureDetector(
  onHorizontalDragUpdate: (details) {
    // 드래그 이벤트 처리
    print('수평 드래그 중: ${details.localPosition}');
  },
  child: Container(
    width: 100,
    height: 100,
    color: Colors.blue,
  ),
)

이 코드는 사용자가 컨테이너 위에서 수평으로 드래그할 때마다 해당 좌표를 콘솔에 출력하는 기능을 구현한 예제입니다.


Flutter로 인터랙티브한 UI 구현하기

GestureDetector는 Flutter에서 터치 이벤트를 감지하고 다양한 사용자 상호작용을 가능하게 하는 핵심 위젯입니다. 앱의 상호작용 요소가 많아질수록 GestureDetector의 활용도는 더욱 중요해집니다. 이를 통해 사용자와의 소통을 강화하고, 더욱 직관적인 UI를 제공할 수 있습니다.

 

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    const title = 'Gesture Demo';

    return const MaterialApp(
      title: title,
      home: MyHomePage(title: title),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;

  const MyHomePage({super.key, required this.title});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: const Center(
        child: MyButton(),
      ),
    );
  }
}

class MyButton extends StatelessWidget {
  const MyButton({super.key});

  @override
  Widget build(BuildContext context) {
    // The GestureDetector wraps the button.
    return GestureDetector(
      // When the child is tapped, show a snackbar.
      onTap: () {
        const snackBar = SnackBar(content: Text('Tap'));

        ScaffoldMessenger.of(context).showSnackBar(snackBar);
      },
      // The custom button
      child: Container(
        padding: const EdgeInsets.all(12),
        decoration: BoxDecoration(
          color: Colors.lightBlue,
          borderRadius: BorderRadius.circular(8),
        ),
        child: const Text('My Button'),
      ),
    );
  }
}