Draggable
Draggable is the high-level drag source helper.
It takes a visible child and a payload of bytes. When the user begins dragging, the runtime carries those bytes until the drag is dropped onto a DragTarget or Dropzone. That payload is not your source of truth. It is only the message that travels during the drag.
Example
use fission::prelude::*;
let widget: Widget = Draggable {
payload: view.state().card_id.as_bytes().to_vec(),
on_drag_start: Some(start_drag_action),
on_drag_end: Some(end_drag_action),
child: Button {
child: Some(Text::new("Release checklist").into()),
on_press: Some(open_card_action),
..Default::default()
}
.into(),
}
.into();
The payload carries just enough information for the eventual drop target to know what moved. The actual board order, selection state, and persistence rules still belong in GlobalState and reducers.
Field table
| | | |
|---|
| | Bytes attached to the drag. | Required. The reducer on the drop target decodes them from ctx.input.as_internal_drop(). |
| | Visible content that the user drags. | |
| | Action fired when dragging begins. | Defaults to None. Useful for visual state such as highlighting the source item. |
| | Action fired when dragging ends. | Defaults to None, regardless of whether a target accepted the drop. |
What belongs in app state and what belongs in the payload
The payload should identify the thing being dragged. The app state should continue to own everything else: the actual collection order, which target is active, whether a drop is allowed, and what happens after acceptance.
This separation matters because it keeps drag-and-drop deterministic. Rebuilding the tree does not lose the product state, and tests can assert the reducer result without simulating hidden mutable widget internals.
Specific advice
If you only need reordering inside one list, use a small stable id in the payload and let the reducer interpret it. If the dragged data is too large or too fragile to serialize to bytes comfortably, that is usually a sign that the payload is carrying too much product logic.
Production checklist
For Draggable, review the fields that change behavior before treating the widget as finished: payload, child, on_drag_start, on_drag_end. The goal is to make the product rule visible in state and actions, not hidden inside ad-hoc construction code.
Bind on_drag_start, on_drag_end to explicit reducer actions and test that the reducer handles unavailable, duplicate, or invalid input safely.
When child widgets are generated from data, give reordered or filtered rows an explicit WidgetId so retained local state and scroll behavior do not drift between items.
Check the semantics tree for the user-facing label or role that makes this widget understandable without relying only on pixels.
Add at least one component or harness test that confirms the visible text, semantic role, action dispatch, and layout constraint that matter for this widget in context.
If a screen starts repeating the same Draggable setup, extract a named component around this widget. That keeps the reference API small while making product code easier to read and safer for generated code to copy.
DragTarget, Dropzone, GestureDetector, and Button.