← Назад к вопросам

Что такое Hit Test?

1.0 Junior🔥 122 комментариев
#Архитектура Flutter

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Hit Test в Flutter

Hit Test — это механизм определения, какой виджет или элемент интерфейса должен получить событие касания (tap), когда пользователь прикасается к экрану. Это критически важная часть архитектуры событий во Flutter.

Как работает Hit Test

Когда пользователь касается экрана в точке координат (x, y), Flutter запускает процесс hit testing:

  1. Начало с корня — процесс начинается с корневого виджета
  2. Обход дерева — Flutter рекурсивно проходит по дереву виджетов снизу вверх (bottom-up)
  3. Проверка границ — для каждого элемента проверяется, попадает ли точка касания в его границы
  4. Формирование цепи — создаётся цепь (hit path) элементов, которые были попаданы
  5. Обработка событий — события распространяются по цепи от самого глубокого виджета вверх

Пример: простой Hit Test

class MyButton extends StatelessWidget {
  final VoidCallback onTap;
  final String label;

  const MyButton({required this.onTap, required this.label});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        width: 100,
        height: 50,
        decoration: BoxDecoration(
          color: Colors.blue,
          borderRadius: BorderRadius.circular(8),
        ),
        child: Center(
          child: Text(label),
        ),
      ),
    );
  }
}

Когда пользователь нажимает на кнопку, GestureDetector выполняет hit test и определяет, что касание произошло в пределах его границ, после чего вызывает onTap.

Переопределение Hit Test

Иногда нужно кастомизировать поведение hit test. Это делается через метод hitTest в класс RenderObject:

class CustomButton extends LeafRenderObjectWidget {
  final VoidCallback onTap;
  final double size;

  const CustomButton({
    required this.onTap,
    required this.size,
  });

  @override
  RenderObject createRenderObject(BuildContext context) {
    return RenderCustomButton(onTap: onTap, size: size);
  }
}

class RenderCustomButton extends RenderBox {
  final VoidCallback onTap;
  final double size;

  RenderCustomButton({
    required this.onTap,
    required this.size,
  });

  @override
  void performLayout() {
    size = Size(size, size);
  }

  @override
  bool hitTest(BoxHitTestResult result, {required Offset position}) {
    // Кастомная логика: например, определяем круг вместо прямоугольника
    final distance = (position - size.center(Offset.zero)).distance;
    if (distance <= size.width / 2) {
      result.add(BoxHitTestEntry(this, position));
      return true;
    }
    return false;
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    context.canvas.drawCircle(
      offset + size.center(Offset.zero),
      size.width / 2,
      Paint()..color = Colors.blue,
    );
  }
}

Практические применения

1. Увеличение области касания

GestureDetector(
  onTap: () {},
  child: Padding(
    padding: EdgeInsets.all(20), // Расширяем область для касания
    child: Icon(Icons.close),
  ),
)

2. Прозрачные области, которые не реагируют на касание

IgnorePointer(
  ignoring: true,
  child: Container(
    color: Colors.transparent,
    child: Text("Это не кликабельно"),
  ),
)

3. Абсорбирование событий касания

AbsorbPointer(
  absorbing: isDisabled,
  child: Button(onTap: action),
)

Важные моменты

  • Hit test — это отдельно от render. Размер для hit test может отличаться от визуального размера
  • Порядок имеет значение. Виджеты, расположенные сверху, имеют приоритет при hit test
  • Производительность. Глубокие деревья виджетов замедляют hit test
  • Opacity не влияет на hit test. Полностью прозрачный виджет всё ещё может получать события (используй IgnorePointer)

Освоение hit test критически важно для создания правильной обработки пользовательского ввода во Flutter.