Introduction
Drag and drop is a natural interaction pattern on mobile that improves discoverability and efficiency. In Flutter, the framework supplies focused primitives to implement drag sources and drop targets with predictable behavior across platforms. This tutorial walks through the core APIs, a minimal implementation, state coordination, and UX considerations to build robust drag-and-drop interactions for mobile development with Flutter.
Core APIs
Flutter's primary building blocks for drag-and-drop are Draggable (and LongPressDraggable) for drag sources and DragTarget for drop targets. Draggable wraps any widget and exposes a data object that travels with the drag. DragTarget receives that data and provides callbacks to control acceptance, show hover states, and finalize the operation.
Key members to know:
Draggable: data, child, feedback, childWhenDragging, onDragStarted, onDragEnd.
DragTarget: builder, onWillAccept, onAccept, onAcceptWithDetails.
Keep the data small and serializable: prefer identifiers or lightweight objects rather than big models to reduce memory pressure during long presses and cross-widget operations.
Implementing a Drag Source
A simple Draggable turns any widget into a drag source. Provide a feedback widget that represents the item under the finger. For mobile development, scale and opacity adjustments make the feedback feel tactile. childWhenDragging lets you show a placeholder where the item originated.
Example minimal Draggable:
Draggable<String>(
data: 'item-1',
feedback: Material(elevation: 6, child: Text('item-1')),
child: ListTile(title: Text('Item 1')),
childWhenDragging: Opacity(opacity: 0.3, child: ListTile(title: Text('Item 1'))),
)Avoid long build work inside feedback. Use Material or DecoratedBox if you need elevation or rounded corners so feedback renders consistently across platforms.
Building Drop Targets
DragTarget exposes a builder that receives three sets: candidate data currently hovering, accepted data already dropped, and rejected data. Use onWillAccept to gate acceptance (return true/false), and onAccept or onAcceptWithDetails to finalize the drop.
A simple DragTarget that only accepts specific IDs:
DragTarget<String>(
onWillAccept: (data) => data?.startsWith('item-') ?? false,
onAccept: (data) => setState(() => droppedItems.add(data)),
builder: (context, candidateData, rejectedData) => Container(
height: 120,
color: candidateData.isNotEmpty ? Colors.green[100] : Colors.grey[200],
child: Center(child: Text('Drop here')),
),
)Use candidateData to provide immediate visual feedback while the user is hovering. For mobile, because touch precision is lower, make targets large enough and provide clear hover states (color, scale, or glow).
State Coordination and Patterns
There are two common patterns for state when moving items between widgets: move (transfer) and copy. Move removes the item from the source; copy leaves the source intact. Implement move by removing the item from the source collection in Draggable's onDragCompleted or in DragTarget's onAccept. Prefer finalizing state changes in onAccept/onAcceptWithDetails to avoid race conditions.
When multiple drop targets exist, use the data payload to include the origin ID so the target can validate cross-list moves. Example payload: Map with keys {"id": "item-1", "origin": "listA"}.
For complex interactions, centralize shared state with Provider, Riverpod, or a parent StatefulWidget. That avoids tightly coupling Draggable and DragTarget and makes undo or animation easier.
UX Considerations and Performance
Feedback design: use a lightweight widget for feedback. Wrap it in Material if you want elevation.
Hit targets: mobile users need larger touch targets; increase margins or provide snap zones.
Animations: use AnimatedContainer or AnimatedOpacity in DragTarget builder to smoothly transition hover states.
Accessibility: provide alternate controls for users who can't drag (context menus, long-press menus, or explicit move buttons).
Performance: keep data small; avoid rebuilding large trees during drag. childWhenDragging prevents a costly rebuild of the source.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Implementing drag and drop in Flutter for mobile development is straightforward once you understand Draggable and DragTarget. Keep the data payload compact, finalize state changes in onAccept, and design feedback and target states for touch. With these patterns you can build lists, kanban boards, or custom rearrangement UIs that feel native on mobile while staying cross-platform with Flutter.