Implementing Drag & Drop Between Widgets in Flutter
Sep 30, 2025



Summary
Summary
Summary
Summary
This tutorial explains Flutter's Draggable and DragTarget APIs for mobile development, showing minimal code for drag sources and drop targets, state coordination patterns (move vs copy), and UX best practices like feedback design, hit target sizing, and performance tips. Finalize state in onAccept and keep the payload lightweight.
This tutorial explains Flutter's Draggable and DragTarget APIs for mobile development, showing minimal code for drag sources and drop targets, state coordination patterns (move vs copy), and UX best practices like feedback design, hit target sizing, and performance tips. Finalize state in onAccept and keep the payload lightweight.
This tutorial explains Flutter's Draggable and DragTarget APIs for mobile development, showing minimal code for drag sources and drop targets, state coordination patterns (move vs copy), and UX best practices like feedback design, hit target sizing, and performance tips. Finalize state in onAccept and keep the payload lightweight.
This tutorial explains Flutter's Draggable and DragTarget APIs for mobile development, showing minimal code for drag sources and drop targets, state coordination patterns (move vs copy), and UX best practices like feedback design, hit target sizing, and performance tips. Finalize state in onAccept and keep the payload lightweight.
Key insights:
Key insights:
Key insights:
Key insights:
Core APIs: Draggable and DragTarget are the primary primitives; use data, feedback, and onWillAccept/onAccept for control.
Drag Source: Provide lightweight feedback and a childWhenDragging placeholder to keep UI responsive during drag.
Drop Target: Use candidateData in the builder for hover visuals and finalize moves in onAccept/onAcceptWithDetails.
State Management: Centralize shared state for complex moves; include origin IDs in payloads to validate transfers.
UX & Performance: Make targets touch-friendly, animate hover states, keep payloads small, and avoid expensive rebuilds.
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.
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.
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.











