Как предотвратить запуск onTapDown на родительских виджетах GestureDetector? - PullRequest
0 голосов
/ 09 ноября 2018

У меня есть стек, в котором можно перемещать несколько виджетов. Кроме того, контейнер, в котором находится стек, имеет GestureDetector для запуска по onTapDown и onTapUp. Я хочу, чтобы эти события onTap запускались только тогда, когда пользователь нажимает за пределы виджета в стеке. Я пробовал следующий код:

class Gestures extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _GesturesState();
}

class _GesturesState extends State<Gestures> {
  Color background;
  Offset pos;

  @override
  void initState() {
    super.initState();
    pos = Offset(10.0, 10.0);
  }

  @override
  Widget build(BuildContext context) => GestureDetector(
    onTapDown: (_) => setState(() => background = Colors.green),
    onTapUp: (_) => setState(() => background = Colors.grey),
    onTapCancel: () => setState(() => background = Colors.grey),
        child: Container(
          color: background,
          child: Stack(
            children: <Widget>[
              Positioned(
                top: pos.dy,
                left: pos.dx,
                child: GestureDetector(
                  behavior: HitTestBehavior.opaque,
                  onPanUpdate: _onPanUpdate,
//                  onTapDown: (_) {},  Doesn't affect the problem
                  child: Container(
                    width: 30.0,
                    height: 30.0,
                    color: Colors.red,
                  ),
                ),
              )
            ],
          ),
        ),
      );

  void _onPanUpdate(DragUpdateDetails details) {
    RenderBox renderBox = context.findRenderObject();
    setState(() {
      pos = renderBox.globalToLocal(details.globalPosition);
    });
  }
}

Однако, когда вы начинаете перетаскивать виджет, также запускается onTap самого внешнего контейнера, в результате чего фон на мгновение становится зеленым. Настройки HitTestBehavior.opaque не работают так, как я ожидал. Также не добавляется обработчик для onTapDown к виджету в стеке.

Итак, как я могу предотвратить запуск onTapDown на самом внешнем GestureDetector, когда пользователь взаимодействует с виджетом внутри стека?

Обновление:

Еще более простой пример проблемы, с которой я сталкиваюсь:

GestureDetector(
  onTapDown: (_) {
    print("Green");
  },
  child: Container(
    color: Colors.green,
    width: 300.0,
    height: 300.0,
    child: Center(
      child: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTapDown: (_) {
          print("Red");
        },
        child: Container(
          color: Colors.red,
          width: 50.0,
          height: 50.0,
        ),
      ),
    ),
  ),
);

Когда я касаюсь и удерживаю красный контейнер, печатаются и «Красный», и «Зеленый», даже если внутренний GestureDetector имеет HitTestBehavior.opaque.

Ответы [ 2 ]

0 голосов
/ 25 июля 2019

Как объяснил один из разработчиков Flutter: https://github.com/flutter/flutter/issues/18450#issuecomment-397372078

opaque просто означает, что весь размер детектора жестов является областью попадания, это не мешает ребенку быть пораженным. Чтобы заблокировать попадания в детей, используйте IgnorePointer или AbsorbPointer.

Вам нужно будет добавить какое-то условие, чтобы обернуть ваш дочерний виджет с IgnorePointer, чтобы ваш лучший GestureDetector мог поглощать сигналы.

В вашем примере добавьте IgnorePointer между этими виджетами:

GestureDetector(
  onTapDown: (_) {
    print("Green");
  },
  child: IgnorePointer(
    child: Container(
      color: Colors.green,
      width: 300.0,
      height: 300.0,
      child: Center(
        child: GestureDetector(
          behavior: HitTestBehavior.opaque,
          onTapDown: (_) {
            print("Red");
          },
          child: Container(
            color: Colors.red,
            width: 50.0,
            height: 50.0,
          ),
        ),
      ),
    ),
  ),
);
0 голосов
/ 12 ноября 2018

В этом ответе я решу более простой пример, который вы привели.Вы создаете следующую иерархию виджетов:

 - GestureDetector       // green
   - Container
     - Center
       - GestureDetector // red
          - Container

Поэтому red GestureDetector является дочерним виджетом green GestureDetector. зеленый GestureDetector имеет значение по умолчанию HitTestBehavior : HitTestBehavior.deferToChild.Вот почему onTapDown запускается для обоих контейнеров.

Цели, которые подчиняются своим детям, получают события в пределах своих границ, только если один из их детей поражен тестом на попадание.

Вместо этого вы можете использовать стек для создания вашего пользовательского интерфейса:

 - Stack
   - GestureDetector // green
     - Container
   - GestureDetector // red
     - Container

Эта структура приведет к следующему исходному коду.Выглядит то же самое, но поведение, которое вы хотели:

Stack(
  alignment: Alignment.center,
  children: <Widget>[
    GestureDetector(
      onTapDown: (_) {
        print("Green");
      },
      child: Container(
        color: Colors.green,
        width: 300.0,
        height: 300.0,
      ),
    ),
    GestureDetector(
      onTapDown: (_) {
        print("Red");
      },
      child: Container(
        color: Colors.red,
        width: 50.0,
        height: 50.0,
      ),
    )
  ],
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...