如何将 Taps 传递给顶部小部件下方的小部件?

Posted

技术标签:

【中文标题】如何将 Taps 传递给顶部小部件下方的小部件?【英文标题】:How to pass Taps to widgets below the top widget? 【发布时间】:2020-01-15 16:21:16 【问题描述】:

我正在尝试在 Flutter 中实现一个非常简单的Card Paginator Component(单卡)版本。

这是我创建的基本演示示例:

class CardComponent extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Stack(
      children: [
        Positioned(
          top: 20.0,
          left: 20.0,
          child: 
          GestureDetector(
          // onTap should run while the text is visible on the screen
          // The text will become invisible when the Listview gest scrolled upwards
          onTap: ()print('text tapped');,
            child:
          Container(
          alignment:Alignment.topCenter,
          color: Colors.blueAccent,
        // This is fixed Text over which the card should scroll
        child: Text('Test Button')))),
        ListView(
          children: [
            //  This margin in first empty container pushes the whole card downward and makes the 'Test Button visible'
            Container(margin: EdgeInsets.only(top: 50.0)),
            // This is where the scrolling card actually starts
            Container(color: Colors.cyan[100], height: 120.0, width: 20.0),
            Container(color: Colors.cyan, height: 120.0, width: 20.0),
            Container(color: Colors.cyan[100], height: 120.0, width: 20.0),
            Container(color: Colors.cyan, height: 120.0, width: 20.0),
            Container(color: Colors.cyan[100], height: 120.0, width: 20.0),
            Container(color: Colors.cyan, height: 120.0, width: 20.0),
          ]
        )
      ],
    );
  


我面临的问题是,当卡片没有向上滚动时,我无法点击按钮。由于空的虚拟容器将可滚动的ListView 向下推,因此固定的“测试按钮”在屏幕上可见。然而,由于ListView 默认覆盖全屏,因此onTap 在可见时不会在点击按钮时运行。

如果我将 ListView/SinglleChildScrollViewIgnorePointer 类包围起来,整个滚动行为就会停止,并且对 ListView 子级的任何点击都会停止工作,这是不需要的。

如何在允许在 ListView 中滚动/点击的同时点击按钮(堆栈中的向后小部件)?或者我应该以不同的方式来构建Card Paginator Component?

【问题讨论】:

我认为问题在于命中事件仅传递给直接子级。 Positioned 不是 ListView 的子对象,但 ListView 捕获了命中事件。您需要找到一种方法将命中事件传递给您的Stack 的所有孩子。 @creativecreator 或可能没有帮助,但当卡片向上滚动并且有人点击下方按钮所在的位置时,可能会导致问题。此外,找不到将命中事件传递给所有小部件的方法。 AbsorbPointerIgnorePointer 是我所知道的两个基于命中事件的小部件。 我认为您需要实现自定义逻辑来捕获到达Stack 的所有命中事件,然后手动决定要将其传递给Stack 的哪些子代。老实说,如果您不熟悉RenderObjects(在本例中为RenderProxyBox),那么尝试快速实现它不会是一个有趣的时间。 好的。如果你能提供一个基本的解决方案,我会试着从那里开始。完全不熟悉RenderObjects。 你也可以试试这个吗? gist.github.com/Nash0x7E2/08acca529096d93f3df0f60f9c034056 【参考方案1】:

Flutter: How to make a ListView transparent to pointer events (but not its non-transparent contents)?的可能重复

您需要在 ListView 中使用覆盖 onTapUpGestureDetector 包裹您的空 Container,以便您可以捕获屏幕上的点击位置:

GestureDetector(
  onTapUp: (TapUpDetails tapUpDetails) 
    print("onTapUp global: " + tapUpDetails.globalPosition.toString());
  ,

然后,您需要使用小部件的键来获取要点击的ListView 后面的小部件矩形:

RenderBox renderBox = _key.currentContext.findRenderObject();
Offset topLeftCorner = renderBox.localToGlobal(Offset.zero);
Size size = renderBox.size;
Rect rectangle = topLeftCorner & size;

最后你可以计算空容器上的点击是否在后面的小部件的矩形内,然后调用你的代码,就好像你在后面的小部件上有一个GestureDetector

// if tap is within boundaries of the background widget
if (rectangle.contains(tapUpDetails.globalPosition)) 
  // your code here

【讨论】:

在第 2 步中,_key 之前是否已定义为我们正在检查的小部件的键?如果是这样,您可以考虑在第 2 步的开头添加该赋值语句以清楚起见。如果不是,_key 是如何定义的?【参考方案2】:

我不知道您是否希望根据当前代码使用按钮修复栏高度,但我可以根据您共享的 Card Paginator Component 帮助您进行设计。

为此,您可以使用 CustomScrollViewSliverAppBar

这里是代码

   CustomScrollView(
      slivers: <Widget>[
        SliverAppBar(
          backgroundColor: Colors.white,
          expandedHeight: 200,
          centerTitle: true,
          pinned: true,
          elevation: 0,
          flexibleSpace: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Expanded(
                child: Center(
                  child: Container(
                      padding: const EdgeInsets.all(10.0),
                      child: CircleAvatar(
                        backgroundColor: Colors.grey.shade400,
                        child: Image.asset("assets/user.png"),
                        maxRadius: 80,
                      )),
                ),
              ),
            ],
          ),
        ),
        SliverToBoxAdapter(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Center(child: Text("Aakash Kumar", style: Theme.of(context).textTheme.headline,)),
          ),
        ),
        SliverFixedExtentList(
          itemExtent: 150.0,
          delegate: SliverChildListDelegate(
            [
              Container(color: Colors.red),
              Container(color: Colors.purple),
              Container(color: Colors.green),
              Container(color: Colors.orange),
              Container(color: Colors.yellow),
              Container(color: Colors.pink),
            ],
          ),
        ),
      ],
    );

希望这会有所帮助...

【讨论】:

以上是关于如何将 Taps 传递给顶部小部件下方的小部件?的主要内容,如果未能解决你的问题,请参考以下文章

如何将小部件尺寸传递给 __init__

如何在 DraggableScrollableSheet 小部件中固定前两个最顶部的小部件?

在 Qt 小部件中的视频顶部绘画

将固定小部件放在 TabBarView 上方

Flutter:如何在构造函数中传递值,以便我可以重用我的小部件?

如何在从另一个小部件继承的小部件中设置数据?