如何模拟波动效果并在Flutter中执行onPressed按钮?

时间:2020-09-02 18:25:00

标签: flutter

我正在尝试模仿Flutter中MaterialButton上的波纹敲击行为。本质上,我希望能够调用任意函数(例如tapTargetWithEffect(GlobalKey target)),并在给定的GlobalKey和要执行的onPressed回调下看到按钮上的涟漪效应。

我在指针测试中看到,可以通过WidgetsBinding分配指针事件以触发涟漪效应。如何执行onPressed回调?

1 个答案:

答案 0 :(得分:1)

这是幼稚的实现方式。

  1. 首先,找到分配给给定GlobalKey的RenderBox
  2. 然后从语义上检查它是否为按钮(即可以按/轻按)
  3. 在RenderBox上执行点击测试,以获取指定指针位置下的小部件(HitTestEntry对象)的列表
  4. 查找已分配了onTap个回调的目标(这些回调将实现RenderSemanticsGestureHandler),首先获取并分配给tappable
  5. 查找已分配了onPointerDown个回调的目标(这些回调将实现RenderPointerListener),首先获取并分配给clickable
  6. 调用onTap回调以执行按钮的onPressed
  7. 用正确的onPointerDown呼叫PointerDownEvent以显示涟漪
    1. PointerDownEvent接受position参数,使其成为原始RenderBox的中心
  8. 通过PointerDownEvent在应用程序边界内的某个地方调度PointerMoveEvent,以取消WidgetsBinding.instance.dispatchEvent在所需的持续时间之后
    1. 在此之前,您必须执行点击测试

请注意,hitTest()函数接受BoxHitTestResult,其中将包含命中测试的结果:)

enter image description here

示例实现:

  void tapTargetWithEffect(GlobalKey target) {
    final RenderBox renderBox = target.currentContext.findRenderObject();
    final child = renderBox as RenderSemanticsAnnotations;
    if (child.button) {
      final size = renderBox.size;
      final center = renderBox.localToGlobal(size.center(Offset.zero));
      final hitResult = BoxHitTestResult();
      child.hitTest(hitResult, position: size.center(Offset.zero));
      final pointerDown = PointerDownEvent(
        position: center,
      );
      final tappableL = hitResult.path
          .where((element) => element.target is RenderSemanticsGestureHandler)
          .map((element) => element.target as RenderSemanticsGestureHandler)
          .toList();
      final clickableL = hitResult.path
          .where((element) => element.target is RenderPointerListener)
          .map((e) => e.target as RenderPointerListener)
          .toList();

      if (tappableL.isEmpty) {
        print('No RenderSemanticsGestureHandler available');
        return;
      }

      if (clickableL.isEmpty) {
        print('No RenderPointerListener available');
        return;
      }

      final tappable = tappableL.first;
      final clickable = clickableL.first;
      clickable.onPointerDown?.call(pointerDown);
      tappable.onTap?.call();

      // cancel the pointer down event
      Future.delayed(Duration(milliseconds: 500)).then((value) {
        print('Cancelling the pointer down event');
        final result = BoxHitTestResult();
        WidgetsBinding.instance.hitTest(result, Offset.zero);
        WidgetsBinding.instance.dispatchEvent(PointerMoveEvent(), result);
      });
    }
  }