Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Callback on drag complete #1

Closed
myleftshoe opened this issue May 25, 2018 · 12 comments
Closed

Callback on drag complete #1

myleftshoe opened this issue May 25, 2018 · 12 comments

Comments

@myleftshoe
Copy link

Hi Matej,
Thanks for this, been using it for a while now without any problems - more than a proof of concept, it's great! Fluid too!

I want to write the reordered items to a backend. At the moment I am doing it async in the onReorder callback which is not ideal because it's called for each position change. i.e. while dragging. Would prefer to do on drag complete, i.e. when the user releases the drag and the item is in the final position.

An onReorderComplete or onDragComplete callback might be useful.

@knopp
Copy link
Owner

knopp commented May 31, 2018

I definitely like the idea. I'll add the callback as soon as I get back to flutter related work.

@ffeu
Copy link

ffeu commented Jul 11, 2018

Hi, just adding to the idea, the onReorderComplete could provide as a parameter the items which were affected. For instance, in the example bellow, moving down the item 2 affects only 3 and 4.

1                1
2                3 --+
3                4 --+ only these 3 were affected
4                2 --+  
5                5

BTW, I think it's worth mentioning this feature request flutter/flutter#16763 .

@knopp
Copy link
Owner

knopp commented Nov 25, 2018

Callback is there now (onReorderDone).

@jwehrle
Copy link

jwehrle commented Sep 5, 2019

I'm using your example in a Cupertino app and while onReorder is called, onReorderDone is never called. The dragging overlay item is never removed and the reorder listener can't be triggered again.

@knopp
Copy link
Owner

knopp commented Sep 5, 2019

Is there any exception in logs? Looks like something goes wrong during reorder finish.

@jwehrle
Copy link

jwehrle commented Sep 5, 2019

No errors in log. I'm going to debug more in depth to see if I can figure out what's happening.

@jwehrle
Copy link

jwehrle commented Sep 5, 2019

Result of debugging into end() of _ReorderableListState.

end(DragEndDetails details) async {
    if (_dragging == null) { // DEBUG value: _dragging is not null
      return;
    }

    _hapticFeedback();
    if (_scrolling) { // DEBUG value: _scrolling is false
      var prevDragging = _dragging;
      _dragging = null;
      SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
        _dragging = prevDragging;
        end(details);
      });
      return;
    }

    if (_scheduledRebuild) { // DEBUG value: _scheduledRebuild returns false
      SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
        if (mounted) end(details);
      });
      return;
    }

    this._scrollable.position.removeListener(this._scrolled);

    var current = _items[_dragging];  // DEBUG value: current is null
    if (current == null) return; // DEBUG value: So we leave method.

    final originalOffset = _itemOffset(current);
    final dragProxyOffset = _dragProxy.offset;

    _dragProxy.updateWidget(current.widget
        .childBuilder(current.context, ReorderableItemState.dragProxyFinished));

    _finalAnimation = new AnimationController(
        vsync: this,
        lowerBound: 0.0,
        upperBound: 1.0,
        value: 0.0,
        duration: Duration(milliseconds: 300));

    _finalAnimation.addListener(() {
      _dragProxy.offset =
          lerpDouble(dragProxyOffset, originalOffset, _finalAnimation.value);
      _dragProxy.shadowOpacity = 1.0 - _finalAnimation.value;
    });

    _recognizer?.dispose();
    _recognizer = null;

    await _finalAnimation.animateTo(1.0, curve: Curves.easeOut);

    if (_finalAnimation != null) {
      _finalAnimation.dispose();
      _finalAnimation = null;

      final dragging = _dragging;
      _dragging = null;
      _dragProxy.hide();
      current.update();
      _scrollable = null;

      if (widget.onReorderDone != null) {
        widget.onReorderDone(dragging);
      }
    }
  }

I think I have further to dig.

@jwehrle
Copy link

jwehrle commented Sep 6, 2019

It appears to be something about the way I'm setting keys. If I use UniqueKey() the dragging behavior is normal but never ends. If I use ValueKey() with index as in example the dragging behavior is abnormal (the dragging pushes other items around like a multi-item drag) but does end. If I figure this out I'll update.

@knopp
Copy link
Owner

knopp commented Sep 6, 2019

There's definitely something wrong with the keys. It seems like your keys are not stable, i.e. the keys for items change after reorder. What index exactly do you store in the key? Because for each item the example stores the original index. Which doesn't change after reorder. Maybe you're using current index (which changes after each reorder)?

Think of the key as unique identifier of each item. It must not change after reorder. UniqueKey should work, as long as you only create instance for each item once (and then remember it for each item).

@jwehrle
Copy link

jwehrle commented Sep 6, 2019

Maybe it's the StreamBuilder I'm using which rebuilds when other elements change (the reorderable list elements are part of a larger structure) and the keys are being reassigned. Also, these reorderable elements can be changed or deleted remotely. This might be a challenge.

@knopp
Copy link
Owner

knopp commented Sep 6, 2019

If elements are changed or deleted remotely, you can provide cancellation token to the list and call cancelDragging on it. This will interrupt dragging in order to prevent inconsistent results.

As for the rest, you should never need to rebuild the whole list during dragging. From the callback, you should only update your model data. Rebuild of items is triggered automatically.

And for the keys, it really shouldn't be that complicated. Don't your items have any kind of unique identifier?

@jwehrle
Copy link

jwehrle commented Sep 6, 2019

It does, and I'm using them. The reorderable list is a copy of the original list which is updated from the copy list when reorder is complete. I added a boolean flag to reduce the chances of unintended initializations of the copy list and this has helped some. onReorderDone now gets called but the animations seem wrong. The dragging item never drops down back into the list after the drag ends. Wondering if that's because in onReorderDone() I fire off a bloc dispatch to update the original data which then rebuilds the whole screen and it doesn't have a chance?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants