Как работает GestureDetector во Flutter?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
GestureDetector во Flutter
GestureDetector — это виджет, который отслеживает жесты пользователя (нажатия, свайпы, масштабирование и т.д.) и вызывает соответствующие колбэки. Это фундамент всей системы взаимодействия в Flutter.
Как работает GestureDetector?
GestureDetector перехватывает события касания (PointerEvent) от операционной системы и преобразует их в высокоуровневые жесты:
- PointerEvent (от ОС) → тач-координаты, давление, время
- GestureArena — система, которая определяет, какой жест произошел
- Жест (tap, swipe, drag и т.д.) → вызов колбэка
Основные типы жестов
1. Tap (нажатие)
GestureDetector(
onTap: () => print(Один клик),
onDoubleTap: () => print(Двойной клик),
onLongPress: () => print(Долгое нажатие),
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
)
Особенность: onTap и onDoubleTap конкурируют между собой. Если жест может быть двойным — onTap задерживается на ~300ms.
2. Drag (движение)
GestureDetector(
onPanDown: (details) {
print(Начало движения: ${details.globalPosition});
},
onPanUpdate: (details) {
print(Движение: ${details.delta});
},
onPanEnd: (details) {
print(Конец движения: ${details.velocity});
},
onPanCancel: () => print(Отмена),
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
)
Разница между методами:
- onPanDown — палец коснулся
- onPanUpdate — палец движется (вызывается множество раз)
- onPanEnd — палец поднят
- onPanCancel — жест прерван системой
3. Swipe (быстрое движение)
GestureDetector(
onHorizontalDragEnd: (details) {
// velocity.pixelsPerSecond — скорость свайпа
if (details.velocity.pixelsPerSecond.dx > 500) {
print(Свайп вправо);
} else if (details.velocity.pixelsPerSecond.dx < -500) {
print(Свайп влево);
}
},
child: Container(
width: 200,
height: 100,
color: Colors.green,
),
)
4. Scale (масштабирование)
GestureDetector(
onScaleStart: (details) {
print(Начало масштабирования);
},
onScaleUpdate: (details) {
print(Масштаб: ${details.scale});
// Используй для изменения размера виджета
},
onScaleEnd: (details) {
print(Конец масштабирования);
},
child: Transform.scale(
scale: _scale,
child: Image.asset(image.jpg),
),
)
Практический пример: Драгируемая карточка
class DraggableCard extends StatefulWidget {
@override
State<DraggableCard> createState() => _DraggableCardState();
}
class _DraggableCardState extends State<DraggableCard> {
double _x = 0;
double _y = 0;
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: (details) {
setState(() {
_x += details.delta.dx;
_y += details.delta.dy;
});
},
onPanEnd: (details) {
// Можно добавить инерцию (fling)
print(Скорость: ${details.velocity.pixelsPerSecond});
},
child: Transform.translate(
offset: Offset(_x, _y),
child: Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(Перетащи меня!),
),
),
),
);
}
}
Gesture Arena — система разрешения жестов
Когда несколько GestureDetector-ов накладываются, система Gesture Arena решает, кому обработать жест:
Stack(
children: [
GestureDetector(
onTap: () => print(Внешний),
child: Container(
width: 200,
height: 200,
color: Colors.blue,
child: GestureDetector(
onTap: () => print(Внутренний),
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
),
),
],
)
// Результат: "Внутренний" (более специфичный жест побеждает)
Параметры GestureDetector
GestureDetector(
// Нажатия
onTap: () {},
onDoubleTap: () {},
onLongPress: () {},
// Движение вертикальное/горизонтальное
onVerticalDragDown: (details) {},
onVerticalDragUpdate: (details) {},
onVerticalDragEnd: (details) {},
onHorizontalDragDown: (details) {},
onHorizontalDragUpdate: (details) {},
onHorizontalDragEnd: (details) {},
// Общее движение
onPanDown: (details) {},
onPanUpdate: (details) {},
onPanEnd: (details) {},
// Масштабирование
onScaleStart: (details) {},
onScaleUpdate: (details) {},
onScaleEnd: (details) {},
// Настройки
behavior: HitTestBehavior.opaque, // область обнаружения
dragStartBehavior: DragStartBehavior.start, // когда начинать drag
child: Widget(),
)
HitTestBehavior — область обнаружения
// Только на видимом контенте
GestureDetector(
behavior: HitTestBehavior.deferToChild,
onTap: () {},
child: Text(Нажми на текст),
)
// На весь контейнер, даже если пусто
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {},
child: Container(
width: 100,
height: 100,
// Видимый контент может быть меньше!
),
)
// Стандартное поведение
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {},
child: Container(),
)
Проблемы и решения
Проблема 1: onTap не срабатывает
// ❌ Неправильно — Container без размера
GestureDetector(
onTap: () {},
child: Container(),
)
// ✅ Правильно — явно задайте размер
GestureDetector(
onTap: () {},
child: Container(width: 100, height: 100),
)
// ✅ Или используйте opaque
GestureDetector(
onTap: () {},
behavior: HitTestBehavior.opaque,
child: Container(),
)
Проблема 2: Конфликт между onTap и onDoubleTap
// ⚠️ onTap вызовется с задержкой в ~300ms
GestureDetector(
onTap: () => print(Один клик),
onDoubleTap: () => print(Двойной клик),
child: Container(),
)
// Если не нужен двойной клик — удали onDoubleTap
Альтернативы GestureDetector
Для простых случаев используй готовые виджеты:
// Вместо GestureDetector + Container
InkWell(
onTap: () {},
child: Text(Нажми),
)
// Для кнопок
ElevatedButton(
onPressed: () {},
child: Text(Кнопка),
)
// Для слайдера
Slider(
value: _value,
onChanged: (value) {},
)
Сравнение: GestureDetector vs InkWell
| Функция | GestureDetector | InkWell |
|---|---|---|
| Жесты | Все | Основные (tap, long press) |
| Визуальный эффект | Нет | Ripple эффект |
| Простота | Низкая | Высокая |
| Управление | Полная | Стандартная |
Вывод: GestureDetector — это основной инструмент для обработки жестов. Используй для сложных взаимодействий, а для простых случаев предпочитай InkWell и готовые виджеты.