InkWell和GestureDetector,如何让它们工作?

7

我想使用GestureDetector的onTapDown回调函数,同时还想有一个漂亮的InkWell水波纹效果。

这两个能一起使用吗?


我知道我来晚了,但我也遇到了同样的问题。最终我直接使用了InkWell小部件。在底层,它也返回一个GestureDetector。在我的小部件中,我创建了一个构造函数,它接受一个VoidCallback onTap。在InkWell onTap事件中,我只是调用了这个提供的回调函数。 - Nightking
5个回答

13
如果您想无条件地处理指针按下事件而不进行手势消歧义gesture disambiguation,则可以将InkWell作为Listener的子元素,并设置onPointerDown处理程序。
例如:
new Listener(
  onPointerDown: (e) { print('onPointerDown'); },
  child: new InkWell(
    child: new Text('Tap me'),
    onTap: () { print('onTap'); }
  ),
),

在InkWell中添加一个onTapDown处理程序可能是有意义的。


10

您可以通过将HitTestBehavior传递给GestureDetector,并将其值设置为"translucent"来使其变为“非阻塞”。

或者您可以使用Material.of(context)自行触发它。


2

在 Rémi 建议将 HitTestBehavior.translucent 行为添加到您的 GestureDetector 上之后,这是我解决您问题的方法:

Material(
    child: GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTapDown: (details) {
            //Do something
        },
        child: InkWell(
            onTap: () {},
            child: Container(height: 20.0, width: 20.0, color: Colors.red),
        ),
    ),
),

我使用类似的方法来给我的InkWell添加了onLongPressStartonLongPressEnd事件。


InkWell现在有onTapDown回调函数。但是实现onSecondaryTap等仍然很有用。然而,我测试了HitTestBehavior.translucent和HitTestBehavior.opaque没有影响。 - YouJiacheng
请参阅 https://api.flutter.dev/flutter/widgets/GestureDetector-class.html 和 https://github.com/flutter/flutter/issues/18450#issuecomment-397372078。 - YouJiacheng

2
InkWell在其内部使用GestureDetector:

https://github.com/flutter/flutter/blob/79b5e5bc8af7d9df3374dfe6141653848d1c03ac/packages/flutter/lib/src/material/ink_well.dart#L560

如果InkWell-Widget正常工作,那么GestureDetector无法很好地工作的原因是,它将其内部GestureDetector的行为设置为"HitTestBehavior.opaque"。这会阻止父小部件上的“事件冒泡”/事件捕获(如果我理解正确的话)。而且由于“行为”是最终的,我们无法自己更改/修复它。

https://github.com/flutter/flutter/blob/79b5e5bc8af7d9df3374dfe6141653848d1c03ac/packages/flutter/lib/src/material/ink_well.dart#L566

正如我在上面的评论中提到的,我将InkWell包装在一个小部件中(该小部件还处理其他内容),并提供了一种传递回调函数的方式,该函数在所需事件上执行。 这是我的示例解决方案:
import 'package:flutter/material.dart';

class CardPreview extends StatefulWidget {   

  final dynamic data;   
  final VoidCallback onTap;

  const CardPreview(this.data, this.onTap);

  CardPreview._(this.data, this.onTap);

  factory CardPreview.fromData(dynamic data, VoidCallback onTap) {
    return new CardPreview(data, onTap);
  }

  CardPreview createState() => new CardPreview(this.data, this.onTap);
}

class CardPreviewState extends State<CardPreview> {   
  final dynamic data;   
  final VoidCallback onTap;

  CardPreviewState(this.data, this.onTap);

  Widget buildCard() {
    return Material(
      color: Colors.transparent,
      child: Ink(
        child: InkWell(
          child: null,
          onTap: () {
            if (this.onTap == null) {
              return;
            }

            this.onTap();
          },
        ),
      ),
    );  
 }

  @override   
  Widget build(BuildContext context) {
    return Container(
      child: buildCard(),
    );   
  } 
}

0

InkWell现在有onTapDown回调函数了

https://api.flutter.dev/flutter/material/InkResponse/onTapDown.html

https://api.flutter.dev/flutter/material/InkWell/InkWell.html

不过,GestureDetector仍然比InkWell具有更多的功能,例如onSecondaryTap。因此,在今天嵌套InkWell和GestureDetector是很有用的。

HitTestBehavior.opaque对子父关系没有影响

https://github.com/flutter/flutter/issues/18450#issuecomment-397372078

https://github.com/flutter/flutter/issues/74733#issuecomment-767859584

简而言之,HitTestBehavior.opaque 只是通过在 HitTest 中返回 true 来防止其后面的兄弟节点接收事件,以便其直接父级不会将事件传递给其下一个子级(按相反顺序),并在 HitTest 中返回 true。(因此,其祖先后面的兄弟节点也不会接收事件。就像其祖先的行为被覆盖为 opaque 一样。)但是其祖先始终接收事件!
备注:如果 A 在 B内部(即 A 是 B 的后代),则 B 将永远不会在 A 的后面
相关来源:

https://api.flutter.dev/flutter/rendering/RenderBox/hitTest.html

https://api.flutter.dev/flutter/rendering/RenderProxyBoxWithHitTestBehavior/hitTest.htmlRenderProxyBoxWithHitTestBehavior_RenderColoredBox使用,它是Container的“color”)

https://api.flutter.dev/flutter/rendering/RenderProxyBoxWithHitTestBehavior/hitTestSelf.html

https://api.flutter.dev/flutter/rendering/RenderBoxContainerDefaultsMixin/defaultHitTestChildren.html

https://api.flutter.dev/flutter/rendering/RenderStack/hitTestChildren.html(使用defaultHitTestChildren

致谢:参考了https://github.com/flutter/flutter/issues/18450#issuecomment-601865975

对于onTap,只有“获胜者”的回调才能触发

请参见https://api.flutter.dev/flutter/widgets/GestureDetector-class.html

将GestureDetector.behavior设置为HitTestBehavior.opaque或HitTestBehavior.translucent对父子关系没有影响:两个GestureDetector都会向手势竞技场发送一个GestureRecognizer,只有一个会获胜。

有些回调函数(例如onTapDown)可能会在识别器赢得竞技场之前触发,而其他回调函数(例如onTapCancel)则会在它输掉竞技场时触发。因此,上面的示例中的父检测器可能会调用一些回调函数,即使它在竞技场中输了。
这很棘手:
如果获胜者有onTap或onTapDown,则输家的onTap将永远不会触发,如果点击太短,以至于在onTapDown触发之前进行了手势竞技场扫描,则onTapDown不会触发。
InkWell具有onTap和onTapDown。

https://github.com/flutter/flutter/blob/468166c713984941c0e5432b7499a1a05a0a0f61/packages/flutter/lib/src/material/ink_well.dart#L1209-L1219

因此,为了获得最佳体验,所有主要的点击回调应该在InkWell上注册。否则,如果GestureDetector获胜,短按不会触发水波纹效果;如果InkWell获胜,短按不会触发Gesture的onTapDown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接