ReorderableForEach
ReorderableForEach is a high-level component in Scripting that provides built-in drag-to-reorder capability.
It preserves the familiar usage pattern of ForEach while adding native support for:
- Drag gesture recognition
- Active item tracking
- Manual reorder callbacks
This allows developers to implement sortable lists and grids with minimal effort.
Typical use cases include:
- Draggable card layouts
- Reorderable grids (
LazyVGrid,LazyHGrid) - User-defined module arrangements
1. Component Definition
2. Generic Constraint
id Is Required and Must Be Stable
The generic type T must satisfy:
This means:
-
Every item must contain a unique and stable
id -
The
idis used to:- Identify the dragged element
- Maintain drag consistency
- Calculate reorder positions correctly
If id values are duplicated or change during runtime, drag behavior will become unstable.
3. Props Reference
3.1 active
Tracks the currently dragged item.
Behavior:
- When dragging starts, the active item is assigned to
active.value - When dragging ends,
active.valueis automatically reset tonull
Typical use cases:
- Highlighting the active item
- Adjusting opacity or scale
- Driving linked animations
- Displaying drag helper UI
3.2 data
The current sortable data source.
Important notes:
ReorderableForEachdoes NOT mutate this array automatically- You must update the order manually inside
onMove - It is strongly recommended to use an observable source:
3.3 builder
Defines how each item is rendered.
Parameters:
The return value must be a valid VirtualNode.
Important:
indexreflects the reordered position- Do not rely on previous fixed indices for logic safety inside
builder
3.4 onMove
Triggered when a drag reorder operation completes.
Parameter reference:
You must perform the full reorder update manually:
- Extract the moving items
- Remove them from the original array
- Insert them at
newOffset - Call
Observable.setValuewith the new array
Standard implementation:
4. Real Purpose of contentShape (Drag Preview Consistency)
From your example:
The primary purpose of this configuration is:
To define the drag preview shape, ensuring that the appearance during dragging matches the non-drag state, such as preserving the
RoundedRectanglecorner radius.
It is used for:
- Defining the drag hit-testing region
- Synchronizing the visual shape during dragging
- Preventing the drag preview from degrading into a default rectangular mask
If this is omitted:
- The drag preview may revert to a plain rectangle
- Visual consistency with custom rounded backgrounds may be lost
5. Full Usage Flow Overview
5.1 Data Model
5.2 Observable Data Source
5.3 Active Drag State
5.4 Item View with Consistent Drag Preview Shape
5.5 Usage Inside LazyVGrid
6. Why ReorderableForEach Is Not Recommended Inside List
Although technically it can be placed inside a List, it is generally discouraged, because List applies a strong set of built-in system behaviors:
- Automatic separators
- Fixed row height management
- Native selection system
- Built-in swipe gestures
- System editing mode
- Cell reuse logic
These behaviors often conflict with custom drag reordering, causing:
- Drag jumping or snapping
- Incorrect hit-testing
- Unwanted system edit mode activation
- Visual desynchronization
Recommended Containers
ScrollViewLazyVGridLazyHGrid- Fully custom layout containers
Not Recommended
List
7. Internal Behavior Summary
ReorderableForEach follows this internal workflow:
-
Builds drag-enabled child nodes from
data -
Uses
dragPreview contentShapeto define the drag hit area and preview shape -
During dragging:
- Automatically updates
active - Continuously recalculates the target insertion index
- Automatically updates
-
On drag completion:
- Calls
onMove - The developer applies the final reorder
- Calls
8. Typical Use Cases
- Custom tool layout sorting
- Draggable dashboard modules
- Reorderable widgets
- Visual task priority organization
- Card-based grid layouts
