Flutter Draggable - Как сбросить Dragable в исходное положение? - PullRequest
1 голос
/ 29 апреля 2020

Обновления:

См. Последние обновления внизу.

Оригинал

I есть работающие виджеты Draggable и DragTarget, за исключением того, что после размещения DragTarget я попадаю в определенную ситуацию, в которой я хочу перестроить DragTarget и переместить Draggable обратно в исходное положение.

Когда я проверяю инспектор Flutter, он показывает мои виджеты, где я их ожидаю, но когда я вызываю своего провайдера для получения следующего набора информации, Draggable остаются в исходном положении, а не возвращаются в исходное положение.

Как видно из последнего кадра изображения, три целевых кадра по-прежнему содержат ссылку на перетаскиваемые виджеты, но я перестраиваю виджеты Draggable и DragTarget при каждом щелчке красного значка IconButton.

Наблюдения

  • Перетаскиваемые объекты остаются в опущенном положении, даже если я обертываю плитки в родительском виджете вместе с получателем.
  • Получатель передает измененные значения данных.
  • DragTargets видны в моем дереве виджетов с помощью Flutter
    Inspector.
  • Когда я попадаю на событие, где у меня более трех Draggables, перетаскиваемые Dragables
    неуместны, в то время как Tiles и DropTargets для других виджетов отображаются, как и ожидалось.

Ожидания:

Когда я сбрасываю свои данные о потребителях, я хочу, чтобы и Tiles, и Targets перерисовывались в своих исходных положениях с новыми данными.

draggable_behavior

DragTarget

@override
  Widget build(BuildContext context) {
    return DragTarget(
      builder:
          (context, List<String> candidateData, List<dynamic> rejectedData) {
        return (isSuccessful) //&& !widget.isPuzzleComplete
            ? Tile(
                title: widget.data,
              )
            : TargetPlaceholder(
                widget: widget,
              );
      },
      onWillAccept: (data) {
        return widget.data == data;
      },
      onAccept: (data) {
        setState(() {
          Provider.of<GameController>(
            context,
            listen: false,
          ).checkPuzzleSolved();
          //activate the animation, may not require setState
          isSuccessful = !widget.isPuzzleComplete;
        });
      },
      onLeave: (data) {
        print('Draggable object left the target area containing data of $data');
      },
    );
  }
}

Draggable

@override
  Widget build(BuildContext context) {
    return Draggable(
      data: widget.title,
//      dragAnchor: DragAnchor.pointer,
      child: isSuccessful ? Container() : TileContent(widget: widget),
      childWhenDragging: Container(),
      feedback: Opacity(
        opacity: 0.7,
        child: TileContent(
          widget: widget,
        ),
      ),
      onDragCompleted: () {
        setState(() {
          Transform(
              transform: Matrix4.translation(_shake()),
              child: TileContent(widget: widget));
        });
      },
      onDragEnd: (details) {
        setState(() {
          isSuccessful = details.wasAccepted;
        });
      },
    );
  }
}

Родительский виджет

 Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              for (var item in targetPattern)
                Consumer<GameController>(builder: (context, gc, _) {
                  return Target(
                    data: item,
                    isPuzzleComplete: gc.isPuzzleComplete,
                  );
                })
            ],
          ),
          SizedBox(
            height: 36,
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              for (var data in scrambledPattern)
                Consumer<GameController>(builder: (context, gc, _) {
                  return Tile(
                    title: data,
                    isPuzzleComplete: gc.isPuzzleComplete,
                  );
                })
            ],
          ),

Обновлено поведение

См. Примечание ниже и второй анимированный GIF для сравнения и сопоставления изменений кода и нового поведения с исходным поведением выше.

После добавления UniqueKey и к Целям, и к плиткам - плитки неправильно отображаются в их исходном местоположении, цели отображаются правильно.

Я хочу сделать следующее:

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

Примечание. Примените следующий код к виджету «Плитка» и «Цель».

key: (gc.isPuzzleComplete == true) ? UniqueKey() : null,

uniquekey-update

Решение

Благодаря напоминанию @ LoVe об использовании UniqueKey и обзору видео команды Flutter Team по ключам; Мне удалось решить эту проблему путем условного добавления UniqueKey во вложенную строку, содержащую как плитки, так и цель.

Из каждого из этих виджетов я смог обновить внешний вид виджетов, используя (isSuccessful || widget.isPuzzleComplete). Мне также удалось удалить логи Consumer c, связанные с захватом лога isPuzzleComplete c, из виджетов Tile / Target и из вызова setState.

В настоящее время поведение на 100% правильное.

//mod to parent row of Tiles
Row(
  key: widget.isPuzzleComplete ? UniqueKey() : null,
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: <Widget>[
   for (var data in scrambledPattern)
     Tile( title: data,
           isPuzzleComplete: widget.isPuzzleComplete,
          ), 
        ],
      ),

//mod to draggable Tile
Draggable(
  key: (widget.isPuzzleComplete == true) ? UniqueKey() : null,
  data: widget.title,
  child: (isSuccessful || widget.isPuzzleComplete)
      ? Container()
      : TileContent(widget: widget),

//mod to drag target 
return DragTarget(
      key: UniqueKey(),
      builder:
          (context, List<String> candidateData, List<dynamic> rejectedData) {
        return (widget.isPuzzleComplete || isSuccessful)
            ? Tile(
                title: widget.data,

Ссылки

Flutter Team Видео по использованию ключей ( ссылка )

1 Ответ

2 голосов
/ 01 мая 2020

Вам нужно будет использовать ключи, примерно такие:

Tile( key:UniqueKey(), title: data, isPuzzleComplete: gc.isPuzzleComplete, ); }

Каждый раз, когда вы используете Row или Column или List виджетов того же типа, и вы обновляете их часто, тогда вы должны использовать Key s, чтобы инфраструктура знала, когда и когда не обновлять виджеты того же типа,

подробнее об использовании ключей здесь

...