变换和偏移动画(颤动)后,GestureDetector 不起作用

Def*_*ine 5 transform hittest gesturedetector flutter

我想创建一个旋转动画,其中包含一组按钮(排列成圆圈),这些按钮从单击的中心按钮旋转和延伸。

虽然动画和旋转工作良好,并且打开按钮和关闭按钮工作顺利并且尽管旋转但仍响应 onTap(),但外部按钮在“onTap”-GestureDetector 方面不起作用。

目标:我想让红色容器可点击(例如GestureDetector)。问题:红色容器对 onTap() 没有反应。

import "dart:developer" as dev;
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:sdg3/Widget/rotation_controller.dart';
import 'package:sdg3/Widget/rotation_menu_button.dart';
import 'package:vector_math/vector_math.dart' show radians, Vector3;

class RadialMenu extends StatefulWidget {
  final Widget open;
  final Widget? close;
  final int startAngle;
  final List<RotationMenuButton> rotationButtons;
  final RotationController? rotationController;

  const RadialMenu({
    Key? key,
    this.rotationController,
    required this.open,
    this.startAngle = 0,
    this.close,
    required this.rotationButtons,
  }) : super(key: key);

  @override
  createState() => _RadialMenuState();
}

class _RadialMenuState extends State<RadialMenu>
    with SingleTickerProviderStateMixin {
  late AnimationController controller;
  late double maxButtonRadius;

  @override
  void initState() {
    super.initState();
    //get "biggest" button
    maxButtonRadius = widget.rotationButtons.map((e) => e.size).reduce(max);

    controller = AnimationController(
        duration: const Duration(milliseconds: 1500), vsync: this)
      ..addListener(() => setState(() {}));
    translation = Tween<double>(
      begin: 0.0,
      end: 1.0,
    ).animate(
      CurvedAnimation(parent: controller, curve: Curves.easeInOutCirc),
    );
    scale = Tween<double>(
      begin: 1.5,
      end: 0.0,
    ).animate(
      CurvedAnimation(parent: controller, curve: Curves.fastOutSlowIn),
    );
    rotation = Tween<double>(
      begin: 0.0,
      end: 360.0,
    ).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(
          0.0,
          0.7,
          curve: Curves.easeInOutCirc,
        ),
      ),
    );
  }

 @override
  Widget build(BuildContext context) {
    Widget open = widget.open;
    Widget? close = widget.close;
    var buttonsMap = widget.rotationButtons.asMap();
    List<Widget> buttons = buttonsMap.entries.map((e) {
      RotationMenuButton button = e.value;
      double singleangle = 360.0 / buttonsMap.length;
      double angle = (e.key * singleangle + widget.startAngle) % 360;
      return LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          double menuwidth = (min(constraints.maxWidth, constraints.maxHeight) -
                  maxButtonRadius) /
              2.0;
          //minimizie space between cicrles
          double dimensionsOfButton = 1 + button.size * (1 - scale.value / 1.5);

          double overlapRadiusOfButton =
              2.0 * sin(radians(singleangle / 2.0)) * menuwidth;
          if (dimensionsOfButton > overlapRadiusOfButton) {
            dimensionsOfButton = overlapRadiusOfButton - 5;
          }

          return Visibility(
            visible: scale.value < 1.0,
            child: Container(
              color: Colors.blue,
              child: Transform(
                transform: Matrix4.identity()
                  ..translate(
                      (translation.value * menuwidth) * cos(radians(angle)),
                      (translation.value * menuwidth) * sin(radians(angle))),
                child: ConstrainedBox(
                  constraints: BoxConstraints(
                      maxHeight: dimensionsOfButton,
                      maxWidth: dimensionsOfButton),
                  child: GestureDetector(
                      onTap: () => dev.log("I was pushed"),
                      child: Container(color: Colors.red)),
                ),
              ),
            ),
          );
        },
      );
    }).toList(growable: false);

    return AnimatedBuilder(
      animation: controller,
      builder: (context, widget) {
        return Transform.rotate(
          angle: radians(rotation.value),
          child: Stack(
            clipBehavior: Clip.none,
            alignment: Alignment.center,
            children: [
              if (close != null)
                Transform.scale(
                  scale: 1.5 - scale.value,
                  child: GestureDetector(
                    onTap: _close,
                    child: close,
                  ),
                ),
              Transform.scale(
                scale: scale.value,
                child: GestureDetector(
                  onTap: _open,
                  child: open,
                ),
              ),
              buttons.first
            ],
          ),
        );
      },
    );
  }

  late final Animation<double> rotation;
  late final Animation<double> translation;
  late final Animation<double> scale;

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  _open() {
    controller.forward();
  }

  _close() {
    controller.reverse();
  }

Run Code Online (Sandbox Code Playgroud)

问题是 dev.log(“我被推了”)。它永远不会出现(墨水池也不起作用,GestureDetector.behaviour 也不起作用)。

这是动画的输出。蓝色容器仅用于可视化,其中容器放置在转换之前,红色容器是转换后正确的“按钮”,它不响应 onTap():

当我删除翻译时:

                  ..translate(
                      (translation.value * menuwidth) * cos(radians(angle)),
                      (translation.value * menuwidth) * sin(radians(angle))),
Run Code Online (Sandbox Code Playgroud)

它有效,但这不是所需的输出。

我的印象是命中测试翻译不正确。你有好主意吗?谢谢!

在此输入图像描述

Def*_*ine 0

我使用了使用定位而不是翻译的解决方法。你\xc2\xb4ll需要一点数学来重新定位它,但它可以与GestureDetector onTap()一起使用(这里没有显示,但它在按钮小部件内部),包括翻译:

\n
          return Visibility(\n            visible: scale.value < 1.0,\n            child: Stack(\n              children: [\n                Positioned(\n                  left: constraints.maxWidth / 2 -\n                      maxButtonRadius / 2 +\n                      (translation.value * menuwidth) * cos(radians(angle)),\n                  top: constraints.maxHeight / 2 -\n                      maxButtonRadius / 2 +\n                      (translation.value * menuwidth) * sin(radians(angle)),\n                  child: SizedBox(\n                    height: dimensionsOfButton,\n                    width: dimensionsOfButton,\n                    child: button,\n                  ),\n                ),\n              ],\n            ),\n          );\n
Run Code Online (Sandbox Code Playgroud)\n