Flutter - GestureDetector не работает с контейнерами в стеке

У меня два контейнера в стеке, и оба контейнера имеют GestureDetector. OnTap для первого контейнера работает нормально, но не работает с другим контейнером. Первый контейнер - это изображение, а второй - зеленый фон, частично выровненный над первым контейнером.

new Stack(
            alignment: Alignment(0.0, 1.44),
            children: <Widget>[
              GestureDetector(
                onTap: () => _openImage(context),
                child: Container(
                  width: 340.0,
                  foregroundDecoration: new BoxDecoration(
                      color: Color.fromRGBO(155, 85, 250, 0.55)),
                  height: 240.0,
                  child: FadeInImage.assetNetwork(
                    placeholder: 'assets/dimlight.png',
                    image: post.imageUrl,
                    fit: BoxFit.cover,
                  ),
                ),
              ),
              new GestureDetector(
                child: new Container(
                  color: Colors.green,
                  child: Row(
                    mainAxisSize: MainAxisSize.max,
                    children: <Widget>[
                      SizedBox(width: 7.0),
                      CircleAvatar(
                        backgroundImage: 
                           new AssetImage("assets/boy.png")
                        radius: 30.0,
                      ),
                      SizedBox(
                        width: 7.0,
                      ),
                      Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          new SizedBox(
                            height: 20.0,
                          ),
                          Text(
                            post.user.name,
                            style: TextStyle(fontWeight: FontWeight.bold),
                          ),
                          Text(
                            getTimeString(post.timestamp.toString()),
                            style: TextStyle(
                                color: Colors.grey, fontSize: 10.0),
                          ),
                        ],
                      ),
                      SizedBox(
                        width: 20.0,
                      ),
                    ],
                  ),
                ),
                onTap: () => _navigateToDetails(context),
              )
            ],
          )

Скриншот макета

введите описание изображения здесь


person Himanshu Yadav    schedule 24.10.2018    source источник


Ответы (12)


Попробуйте установить для свойства behavior GestureDetector значение HitTestBehavior.translucent.

person Athul Sai    schedule 24.02.2019
comment
Да, работает. Почему такого поведения нет по умолчанию? - person Dmitriy Blokhin; 18.07.2019
comment
Обратите внимание, что это не работает с просмотрами страниц. stackoverflow.com/q/49510694/8608146. - person Phani Rithvij; 16.12.2019
comment
В случае, если 2-й контейнер ночует первый контейнер, то есть ли способ получить событие onTap для 1-го контейнера? - person Dhaval Kansara; 14.07.2020
comment
По умолчанию используется HitTestBehavior.deferToChild, если дочерний элемент не равен нулю, и HitTestBehavior.translucent, если дочерний элемент равен нулю. - person nitinku5021a; 25.08.2020
comment
Это не сработало для меня, есть другие предложения? - person Daniel Hernandez; 04.01.2021
comment
Почему это работает? - person SametSahin; 06.02.2021

Использование InkWell вместо GestureDetector решило мою проблему.

person Sobhan Moradi    schedule 05.02.2019
comment
Да, но он добавляет другие (как правило) ненужные части - например, чернила хорошо. Установка behavior - правильный ответ в текущем контексте. - person Alex Semeniuk; 01.08.2019
comment
К вашему сведению, InkWell - это материальный компонент. Он добавляет визуальную / звуковую обратную связь, которая может быть не тем, чего хочет разработчик. Кроме того, приложение может использовать тему iOS Cupertino. Если вас интересуют только события касания / щелчка, GestureDetector - хороший вариант. (с HitTestBehavior.translucent) - person user482594; 05.01.2020
comment
OP дал понять, что они хотят использовать GestureDetector. - person HBG; 20.03.2020

Для меня это была проблема с переполнением элементов за пределами Stack с clipBehavior: Clip.none.

В этом случае элемент виден, но его нельзя коснуться. Поэтому я обновил свой макет, чтобы удалить clipBehavior: Clip.none на моем Stack, и GestureDetector начал работать. ????

person Hugo H    schedule 01.12.2020

В моем случае внутри стека был CustomPainter. Ничего из вышеперечисленного не работало, пока я не дал CustomerPainter размер. Он отображался без явного указания размера, но события касания не запускались:

Container(
      child: CustomPaint(
    size: Size(30, 30),
    painter: RecordingButton(30),
  ))
person stan    schedule 19.04.2020

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

вашего кода недостаточно, поэтому я добавил следующий пример, который может помочь вам понять более четко.

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

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Firebase Auth Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Card(
        margin: EdgeInsets.all(40.0),
        child: new Column(
          mainAxisSize: MainAxisSize.max,
          children: <Widget>[
            GestureDetector(
              onTap: () => print("first container"),
              child: Container(
                width: 340.0,
                foregroundDecoration: new BoxDecoration(
                    color: Color.fromRGBO(155, 85, 250, 0.0)),
                height: 240.0,
                child: FadeInImage.assetNetwork(
                  placeholder: 'images/p1.png',
                  image:
                  "https://www.straitstimes.com/sites/default/files/styles/article_pictrure_780x520_/public/articles/2016/06/15/ST_20160615_LLIMH_2368135.jpg?itok=8Dggu2PM&timestamp=1465926004",
                  fit: BoxFit.cover,
                ),
              ),
            ),
            new GestureDetector(
              child: new Container(
                foregroundDecoration: BoxDecoration(
                    color: Color.fromRGBO(155, 85, 250, 0.4)),
                child: Row(
                  mainAxisSize: MainAxisSize.max,
                  children: <Widget>[
                    SizedBox(width: 7.0),
                    CircleAvatar(
                      backgroundImage: new AssetImage("images/p2.jpg"),
                      radius: 30.0,
                    ),
                    SizedBox(
                      width: 7.0,
                    ),
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          new SizedBox(
                            height: 20.0,
                          ),
                          Text(
                            "sfvgefbv",
                            style: TextStyle(fontWeight: FontWeight.bold),
                          ),
                          Text(
                            "sfvmsfkv",
                            style: TextStyle(
                                color: Colors.grey, fontSize: 10.0),
                          ),
                        ],
                      ),
                    ),
                    new Container(
                      alignment: AlignmentDirectional.centerEnd,
//            todo add here check if not logged in then ask to
                      child: Row(
                        mainAxisSize: MainAxisSize.min,
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: <Widget>[
                          IconButton(
                              icon: Icon(
                                Icons.comment,
                                color: Colors.green,
                              ),
                              onPressed: () => print("message click")),
                          Text(
                            "2",
                            style: TextStyle(
                              color: Colors.green,
                            ),
                          ),
                          SizedBox(
                            width: 10.0,
                          )
                        ],
                      ),
                    ),
                  ],
                ),
              ),
              onTap: () => print("this is second container"),
            ),
            new Expanded(
              child: Container(
                padding: EdgeInsets.all(10.0),
                child: Column(
                  children: <Widget>[
                    Text(
                      "fsvkmfskbnmkffvberk",
                      style: TextStyle(
                          color: Colors.green, fontWeight: FontWeight.bold),
                    ),
                    new Text(
                      "svklmfslkbnernkjrnvkrwjnvrw",
                      maxLines: 6,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
person Viren V Varasadiya    schedule 24.10.2018
comment
Это то, что я уже упоминал в вопросе о том, что контейнер с зеленым фоном имеет GestureDetector и его частично поверх другого контейнера, имеющего фон как изображение (посмотрите на прилагаемое изображение). Проблема в том, что GestureDector работает только в той части, которая находится над первым контейнером, а не на другой половине контейнера зеленого цвета и которая не перекрывается с первым контейнером. - person Himanshu Yadav; 25.10.2018
comment
в любом случае вы должны удалить это перекрытие в своей части проектирования. только так вы сможете достичь желаемого результата. вашего кода недостаточно. пожалуйста, добавьте еще код, связанный с этим. - person Viren V Varasadiya; 25.10.2018
comment
Хорошо, вот ссылка на файл, чтобы узнать о проблеме. gist.github.com/himanshu096/b9f8d4506a024c33218c1529102749 - person Himanshu Yadav; 25.10.2018
comment
я только что обновил свой ответ. Я думаю, что вы используете много виджетов стека, что на самом деле не нужно. Я надеюсь, что этот код решил вашу проблему. если нет, то точно опишите, что вам нужно. - person Viren V Varasadiya; 25.10.2018
comment
Я хочу, чтобы мой второй контейнер (с этим изображением пользователя) частично перекрывал первый контейнер (контейнер с большим изображением). Чтобы дать вам более четкое представление, я только что обновил файл gist. Просто запустите этот код, и это именно тот пользовательский интерфейс, который мне нужен. - person Himanshu Yadav; 26.10.2018

Вам следует попробовать обернуть свой контейнер AbsorbPointer.

person Michel Fortes    schedule 12.04.2021
comment
Большое спасибо. - person Saroj Dahal; 03.06.2021
comment
Будет лучше, если вы приведете какой-нибудь пример - person Problematic Dude; 11.06.2021


Для других, приходящих к этому сообщению, еще одна вещь, которая может вызвать эту проблему, - это то, что AbsorbPointer выше - это иерархия.

person johngray1965    schedule 14.11.2019

Другой GestureDetector в дереве BuildContext

Хорошо, с моей стороны, это было довольно глупо, но поскольку это может случиться со многими из нас, я объясню:

Мое дерево контекста сборки (упрощенное для примера) было таким:

Widget build(BuildContext context) {

return (...) (
    child: GestureDetector(
        onTap: () => _myFunction(),
        child: Container(
            height: 64.0,
            width: 128.0,
            child: (...)

Кажется законным, правда? Но затем я более подробно изучил нижнюю часть своего дерева:

(...)
        onTap: () => _myFunction(),
        child: Container(
            height: 64.0,
            width: 128.0,
            child: TheSourceOfEvil() // <-- whoopsie!

Быстро проверил TheSourceOfEvil и выяснил, что виновником на самом деле был ДРУГОЙ GestureDetector, у которого есть дочерний элемент, который я еще не реализовал onTap: () => {},:

Widget TheSourceOfEvil( {

return (...) (
    child: GestureDetector(
        onTap: () => {}, // override previous GestureDetector
        child: Container(
            child: (...);

Избавился от него, и это мгновенно решило мою загадочную проблему. Надеюсь, это поможет!

person Antonin GAVREL    schedule 08.06.2020
comment
что нам нужны вложенные детекторы жестов? - person codeboi; 25.05.2021

Если вы используете виджет GoogleMap и хотите добавить поле поиска / текстовое поле, вы должны знать, что виджет карты использует ваши жесты. Я делаю примерно так:

final FocusNode _focusNode = new FocusNode();

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.translucent,
      onTap: () {            
        print("tap");
        if (_focusNode.hasPrimaryFocus) {
          _focusNode.unfocus();
          rebuildAfterFocusChange();
        }
      },
      child: Stack(children: [
        AbsorbPointer(absorbing: _focusNode.hasPrimaryFocus, child: StoreMapView()),
        showSearchField(model),
      ]),
    );
  }

Widget showSearchField(StoreFinderViewModel model) {
    return SafeArea(
      child: Padding(
        padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 12.0),
        child: TextField(
          onTap: () {
            rebuildAfterFocusChange();
          },
          onSubmitted: (String input) {
            rebuildAfterFocusChange();
            // do other stuff
            });
          },
          focusNode: _focusNode,
        ),
      ),
    );
  }

  void rebuildAfterFocusChange() {
    setState(() {});
  }

Я использую виджет AbsorbPointer для поглощения щелчков / нажатий, но ТОЛЬКО если мое текстовое поле имеет фокус. Я вызываю свой rebuildAfterFocusChange (); если я нажимаю на свое поле поиска, используйте метод onsubmit текстового поля, чтобы запустить поиск в моем бэкэнде и после того, как я расфокусирую свое текстовое поле.

person basti12354    schedule 09.02.2021

Также рассмотрите виджет Listener. Мне это помогло.
https://api.flutter.dev/flutter/widgets/Listener-class.html
Пример:

Listener(
    onPointerDown: (_) {}, // onTapDown
    onPointerUp: (_) => {}, // onTapUp
    child: Container(),
)
person Rodion Mostovoy    schedule 09.07.2021

Я вижу, что вы вызываете метод обратного вызова вместо того, чтобы назначать его свойству onTap виджета GestureDetector. Передайте имя метода, не называйте его там.

person paulos    schedule 09.01.2019
comment
если мы хотим выполнить функцию в onTap, я думаю, что мы должны это использовать. Если мы передадим ссылку на func. тогда он вообще не будет выполняться. - person Himanshu Yadav; 10.01.2019