работа с сенсорным трепетанием, похожая на зависание - обнаружение касания от разных виджетов без подъема пальца - PullRequest
0 голосов
/ 10 ноября 2019

Предположим, у меня на экране 3 Container с, которые реагируют на прикосновения, меняя цвет. Когда палец пользователя на них, они должны изменить цвет, а при прикосновении к ним они должны вернуться в нормальное состояние. Я хочу, чтобы эти контейнеры реагировали даже тогда, когда касание начинается в произвольной области на экране, вне контейнеров. Так что в основном то, что я ищу, это нечто вроде css hover.

Если я оберну каждый контейнер отдельно GestureDetector, они не будут реагировать на события касания, которые начинаются вне их. По другому вопросу (к сожалению, у меня нет ссылки сейчас) предлагается обернуть все контейнеры одним GestureDetector и назначить GlobalKey каждому, чтобы отличать их друг от друга.

Вот плата , которая обнаруживает жесты прикосновения:

class MyBoard extends StatefulWidget {
  static final keys = List<GlobalKey>.generate(3, (ndx) => GlobalKey());

  ...
}

class _MyBoardState extends State<MyBoard> {
  ...

  /// I control the number of buttons by the number of keys,
  /// since all the buttons should have a key
  List<Widget> makeButtons() {
    return MyBoard.keys.map((key) {
      // preapre some funcitonality here
      var someFunctionality;
      return MyButton(key, someFunctionality);
    }).toList();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (tapDownDetails) {
        handleTouch(context, tapDownDetails.globalPosition);
      },
      onTapUp: (tapUpDetails) {
        finishTouch(context);
      },
      onPanUpdate: (dragUpdateDetails) {
        handleTouch(context, dragUpdateDetails.globalPosition);
      },
      onPanEnd: (panEndDetails) {
        finishTouch(context);
      },
      onPanCancel: () {
        finishTouch(context);
      },
      child: Container(
        color: Colors.green,
        width: 300.0,
        height: 600.0,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: makeButtons(),
        ),
      ),
    );
  }

Вот простая MyButton:

class MyButton extends StatelessWidget {
  final GlobalKey key;
  final Function someFunctionality;
  MyButton(this.key, this.someFunctionality) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Consumer<KeyState>(
      builder: (buildContext, keyState, child) {
        return Container(
          width: 100.0,
          height: 40.0,
          color: keyState.touchedKey == this.key ? Colors.red : Colors.white,
        );
      },
    );
  }
}

В _MyBoardState Вот как я работаю, обнаруживая, к какому MyButton прикоснулись:

MyButton getTouchingButton(Offset globalPosition) {
    MyButton currentButton;
    // result is outside of [isTouchingMyButtonKey] for performance
    final result = BoxHitTestResult();

    bool isTouchingButtonKey(GlobalKey key) {
      RenderBox renderBox = key.currentContext.findRenderObject();
      Offset offset = renderBox.globalToLocal(globalPosition);
      return renderBox.hitTest(result, position: offset);
    }

    var key = MyBoard.keys.firstWhere(isTouchingButtonKey, orElse: () => null);
    if (key != null) currentButton = key.currentWidget;

    return currentButton;
  }

Я использую пакет провайдера и вместо перекомпоновки всего MyBoard с setState только соответствующей частью связанной MyButtonбудет восстановлен. Тем не менее, каждая кнопка имеет прослушиватель, который перестраивается при каждом обновлении, и я использую GlobalKey. На флаттерной документации написано:

Глобальные ключи относительно дороги. Если вам не нужны какие-либо из перечисленных выше функций, попробуйте вместо этого использовать Key, ValueKey, ObjectKey или UniqueKey.

Однако если мы рассмотрим метод getTouchingButton, мне понадобится GlobalKey дляполучить renderBox объект и получить currentWidget. Я также должен сделать hitTest для каждого GlobalKey. Это вычисление повторяется при срабатывании onPanUpdate, что означает, что палец пользователя перемещает пиксель.

Пользовательский интерфейс не обновляется достаточно быстро. Одним касанием (tapDown и tapUp на обычной скорости) вы обычно не видите никаких изменений на MyButton s.

Если мне нужно подвести итог моего вопроса, Как я могу обнаружить один и тот жеприкасаться (не поднимать) от разных виджетов, когда на них более эффективно и элегантно наведен палец?

1 Ответ

0 голосов
/ 15 ноября 2019

Поскольку никто еще не ответил, я делюсь своим собственным решением, которое я выяснил в последнее время. Теперь я вижу визуальную реакцию при каждом прикосновении. Он достаточно быстрый, но все же кажется, что пользовательский интерфейс немного задерживается, и вместо конкретного решения он выглядит немного хакерским. Если кто-то может дать лучшее решение, я могу пометить его как принятый ответ .


Мое решение:

Перво-наперво;так как мы должны обнаружить жест Pan и мы используем onPanUpdate и onPanEnd, я могу стереть onTapDown и onTapUp и вместо этого просто использовать onPanStart. Это также может обнаружить неподвижные простые касания.

Я также больше не использую Provider и Consumer, так как он восстанавливает все Consumer при каждом обновлении. Это действительно большая проблема, когда число MyButton с увеличивается. Вместо того, чтобы держать MyButton простым и глупым, я перенес в него работу с сенсорным управлением. Каждый MyButton содержит данные , если к ним прикасались в данный момент .

Обновленная кнопка выглядит примерно так:

class NewButton extends StatefulWidget {
  NewButton(Key key) : super(key: key);
  @override
  NewButtonState createState() => NewButtonState();
}

class NewButtonState extends State<NewButton> {
  bool isCurrentlyTouching = false;

  void handleTouch(bool isTouching) {
    setState(() {
      isCurrentlyTouching = isTouching;
    });
    // some other functionality on touch
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100.0,
      height: 40.0,
      color: isTouched ? Colors.red : Colors.white,
    );
  }
}

Уведомление что NewButtonState не является частным. Мы будем использовать globalKey.currentState.handleTouch(bool), где мы обнаружим сенсорный жест.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...