Repository navigation
Draggable: onDragUpdate callback #39490
Description
Activity
It's very similar to #16873
- addedframeworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.c: proposalA detailed proposal for a change to FlutterA detailed proposal for a change to Flutter
on Aug 29, 2019 - addedc: new featureNothing broken; request for a new capabilityNothing broken; request for a new capability
on Jan 3, 2020 Any updates on it?
Reacted by Juliano Appel KleinAs a workaround I'm passing a GlobalKey to the feedback widget to get it's position when needed. I can get the change comparing the first position with the current.
As a workaround I'm passing a GlobalKey to the feedback widget to get it's position when needed. I can get the change comparing the first position with the current.
Not sure if it will fit in my case -- I want to update
childWhenDragging's position according tofeedbackwidget position, e.g. make it follow feedback like a shadow :-)@emvaized - See the class I'm using below. It's not pretty, but it works.
When using it, If you wrap your childWhenDragging with a Transform.translate to adjust its position using the offset from onDragUpdated.
Please note that this class is not worried about the target. You may need to adapt it to suits your needs.
import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; class SimpleDraggable extends StatefulWidget { SimpleDraggable({ Key key, this.child, this.onDragStarted, this.onDragUpdated, this.onDragEnd, this.feedback, this.affinity, this.axis, this.feedbackOffset = Offset.zero, this.dragAnchor = DragAnchor.child, this.childWhenDragging, }) : super(key: key); final void Function() onDragStarted; final void Function(Offset offset) onDragUpdated; final Offset feedbackOffset; final void Function(Offset offset) onDragEnd; final Widget feedback; final Axis axis; final Axis affinity; final Widget child; final Widget childWhenDragging; final DragAnchor dragAnchor; @override _SimpleDraggableState createState() => _SimpleDraggableState(); } class _SimpleDraggableState extends State<SimpleDraggable> { Offset _currentPosition; GlobalKey _dragFeedback = GlobalKey(); Future<dynamic> _updateFuture; Offset _startPosition; @override Widget build(BuildContext context) { return Draggable( affinity: widget.affinity, axis: widget.axis, child: widget.child, childWhenDragging: widget.childWhenDragging, dragAnchor: widget.dragAnchor, feedbackOffset: widget.feedbackOffset, feedback: Container( key: _dragFeedback, child: widget.feedback, ), maxSimultaneousDrags: 1, onDragStarted: handleDragStarted, onDragEnd: handleDragEnd, ); } Offset getUpdatePosition() { if (_dragFeedback.currentContext == null) return null; final RenderBox feebackRenderBox = _dragFeedback.currentContext.findRenderObject(); return feebackRenderBox.localToGlobal(Offset.zero); } void scheduleNextUpdate() { if(!this.mounted) return; _updateFuture = Future.delayed(const Duration(milliseconds: 20), () { if (_updateFuture == null) { return; } final position = getUpdatePosition(); if (position != null) { if (_startPosition == null) { _startPosition = position; _currentPosition = position; widget.onDragStarted?.call(); } else { _currentPosition = position; this.widget.onDragUpdated(_currentPosition - _startPosition); } } scheduleNextUpdate(); }); } void handleDragStarted() { scheduleNextUpdate(); } void handleDragEnd(DraggableDetails details) { if (_startPosition != null) { widget.onDragEnd ?.call((_currentPosition ?? _startPosition) - _startPosition); } _updateFuture = null; _startPosition = null; _currentPosition = null; } }Reacted by Max Tsyba and Aditya Mulgundkar@julianoappelklein Wow, your solution actually works great!
I guess it's a perfect workaround until Flutter team will implement this feature. Thank you for sharing!Reacted by Juliano Appel Klein and Aditya MulgundkarHere is a different approach which provides a drop in replacement for Draggable and provides an onDragUpdate property:
import 'package:flutter/gestures.dart'; import 'package:flutter/widgets.dart'; /// Adds an additional property onDragUpdate that receives calls during a drag event /// All other behaviour is inherited from Draggable class TrackingDraggable<T> extends Draggable<T> { final GestureDragUpdateCallback onDragUpdate; TrackingDraggable( {Key key, @required child, @required feedback, data, axis, childWhenDragging, feedbackOffset = Offset.zero, dragAnchor = DragAnchor.child, affinity, maxSimultaneousDrags, onDragStarted, onDraggableCanceled, onDragEnd, onDragCompleted, ignoringFeedbackSemantics = true, this.onDragUpdate}) : super( key: key, child: child, feedback: feedback, data: data, axis: axis, childWhenDragging: childWhenDragging, feedbackOffset: feedbackOffset, dragAnchor: dragAnchor, affinity: affinity, maxSimultaneousDrags: maxSimultaneousDrags, onDragStarted: onDragStarted, onDraggableCanceled: onDraggableCanceled, onDragEnd: onDragEnd, onDragCompleted: onDragCompleted, ignoringFeedbackSemantics: ignoringFeedbackSemantics); /// Hooks into the Draggable start point so we can proxy Drag events and provide updates @protected MultiDragGestureRecognizer<MultiDragPointerState> createRecognizer( GestureMultiDragStartCallback onStart) { var originalRecognizer = super.createRecognizer(onStart); // Intercept the onStart and insert our proxy var originalOnStartFunction = originalRecognizer.onStart; originalRecognizer.onStart = (Offset position) { // Get the original Drag var originalDrag = originalOnStartFunction(position); // Proxy it return _DragProxy(originalDrag, this); }; // We've now inserted our onStart, so return return originalRecognizer; } } class _DragProxy implements Drag { Drag _originalDrag; TrackingDraggable _ourTracker; _DragProxy(this._originalDrag, this._ourTracker); @override void cancel() { _originalDrag.cancel(); } @override void end(DragEndDetails details) { _originalDrag.end(details); } @override void update(DragUpdateDetails details) { if (_ourTracker.onDragUpdate != null) { _ourTracker.onDragUpdate(details); } // Call the original as well. _originalDrag.update(details); } }I would like to be assigned to this issue
This has now been added to the stable channel, hence this issue may be closed.
- added a commit that references this issue
on Feb 9, 2023 - added a commit that references this issue
on Feb 15, 2023 This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.- locked as resolved and limited conversation to collaborators
on Mar 4, 2023 - added a commit that references this issue
on Jul 25, 2025
Use case
I'm creating widget, like free-transform in photoshop
I have main draggable (big rectangle) and small draggable (small circles) in each corner
To be able to update all their positions I had to add onDragUpdate callback to Draggable
Proposal
Draggable should have parameter
Function(DragUpdateDetails) onDragUpdateI can provide a PR if any maintainer approve this as valid feature