4
votes

Comment faire une capture GestureDetector taps dans une pile?

J'essaye de faire fonctionner un GestureDetector dans une pile avec un conteneur dessus, mais le rappel onTap n'est jamais appelé.

Comme vous pouvez le voir, cela ne fonctionne pas même avec HitTestBehavior.translucent

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Stack(
          children: <Widget>[
            GestureDetector(
              behavior: HitTestBehavior.translucent,
              onTap: () {
                print('tap');
              },
              child: Container(color: Colors.blue),
            ),
            Container(color: Colors.white),
          ],
        ),
      ),
    );
  }

Je sais qu'il peut être étrange que je veuille capturer un événement tap sous un autre widget, mais dans mon cas réel, le widget en haut est transparent et a parfois un dégradé.


0 commentaires

3 Réponses :


0
votes

Vous avez fait un conteneur (couleur blanche) couvrant en complétant le conteneur de couleur bleue, donc lorsque vous le touchez, vous l'abandonnez (conteneur blanc) si vous voulez gérer le toucher (conteneur blanc) Mettez un geste détecteur dessus aussi.

un moyen facile de tester le problème avec lequel vous inversez la position du conteneur blanc avec le bleu, et vous verrez le "robinet" se produire

cela fonctionne parfaitement bien:

Scaffold(
  body: Container(
    child: Stack(
      children: <Widget>[
         GestureDetector(
          behavior: HitTestBehavior.translucent,
          onTap: () {
            print('tap on white ');
          },
          child:
        Container(color: Colors.white)),

        GestureDetector(
          behavior: HitTestBehavior.translucent,
          onTap: () {
            print('tap on blue');
          },
          child: 
          SizedBox(
            height: 500,
            child: Container(color: Colors.blue),
          )
        ),
      ],
    ),
  ),
);


1 commentaires

Comme je l'ai dit, il ne s'agit que d'une simplification d'un problème complexe. Je ne peux pas mettre le détecteur de gestes sur la couche supérieure car il y a beaucoup de gestes complexes là-bas. Ce que je veux, c'est détecter le tap sur la deuxième couche.



13
votes

Ok les gars, je pense que j'ai trouvé une solution moi-même. J'espère qu'il existe une solution plus simple mais cela fonctionne pour mon usage. Le problème que j'ai eu est que le widget Stack ne passe pas le test de succès à tous les enfants mais uniquement au premier qui est touché. Ce que j'ai fait, c'est que j'ai réécrit l'algorithme de détection de hit utilisé par le RenderBox de Stack. Je n'avais vraiment pas l'intention d'aller aussi loin et j'attends toujours une meilleure réponse. Voici mon code, utilisez-le à vos risques et périls:

class CustomStack extends Stack {
  CustomStack({children}) : super(children: children);

  @override
  CustomRenderStack createRenderObject(BuildContext context) {
    return CustomRenderStack(
      alignment: alignment,
      textDirection: textDirection ?? Directionality.of(context),
      fit: fit,
      overflow: overflow,
    );
  }
}

class CustomRenderStack extends RenderStack {
  CustomRenderStack({alignment, textDirection, fit, overflow})
      : super(
            alignment: alignment,
            textDirection: textDirection,
            fit: fit,
            overflow: overflow);

  @override
  bool hitTestChildren(BoxHitTestResult result, {Offset position}) {
    var stackHit = false;

    final children = getChildrenAsList();

    for (var child in children) {
      final StackParentData childParentData = child.parentData;

      final childHit = result.addWithPaintOffset(
        offset: childParentData.offset,
        position: position,
        hitTest: (BoxHitTestResult result, Offset transformed) {
          assert(transformed == position - childParentData.offset);
          return child.hitTest(result, position: transformed);
        },
      );

      if (childHit) stackHit = true;
    }

    return stackHit;
  }
}


6 commentaires

Tu es un génie! Merci mec. Je voulais créer une AppBar transparente au-dessus d'un conteneur plein écran avec une pile, mais comme l'AppBar était également rendue en plein écran, elle capturait le clic et les éléments sous-jacents ne répondaient pas. Votre CustomStack l'a fait fonctionner!


Vous êtes le bienvenu! C'est vraiment dommage que nous devions passer par ça pour que ça marche, je pense vraiment que la méthode de détection de hit utilisée par Flutter devrait être retravaillée, c'est tellement plus simple de faire ces choses en CSS ...


Même cas d'utilisation ici, et sans cette solution, je ne peux même pas deviner combien d'heures j'aurais perdu dans cela. Je voterais dix fois si je pouvais. Merci pour cela.


Cela fonctionne parfaitement! Merci! Quelle mission d'avoir un bouton sous une liste encore cliquable.


Merci! Pensez-vous que le fait de permettre à GestureDetector de ne pas absorber les tests de succès aura également du sens? Ie onTap() { print('tap'); return true; } // continue hitTests


Brillant! Merci.



1
votes

Cette solution est très simple et n'utilise aucun hacks ou astuces! Simplement en utilisant des widgets de flutter:

Vous pouvez envelopper votre widget supérieur (dans ce cas, le Container blanc) avec un IgnorePointer et définir la propriété ignoring sur true .

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Stack(
          children: <Widget>[
            GestureDetector(
              behavior: HitTestBehavior.translucent,
              onTap: () {
                print('tap');
              },
              child: Container(color: Colors.blue),
            ),
            IgnorePointer(ignoring:true,child:Container(color: Colors.white)),
          ],
        ),
      ),
    );
  }

Ensuite, votre widget supérieur devient transparent à n'importe quel robinet et aussi, les taps seraient capturés par le widget le plus haut sous le widget supérieur non enveloppé par un IgnorePointer avec ignoring:true .

Par exemple, vous pouvez apporter peu de modifications à votre code pour obtenir le résultat souhaité, comme ceci:

Stack(
  children:[
    (...your down widget which should handle the tap...)

    IgnorePointer(
      ignoring:true,
      child: (... your top widget which must be transparent to any tap ...)
    )
  ]
)

Pour plus d'informations, visitez la page d'aide officielle sur flutter.dev .


0 commentaires