Reorder drag has no visible affordance on dark backgrounds: dragging flag is spent on an invisible shadow
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 330
- Forks
- 76
- Avg merge
- 3h 6m
- Merged PRs (30d)
- 1
Description
Summary
List computes whether a row is being dragged and spends it entirely on
Modifier.shadow(8.dp). On a dark app that shadow is invisible, so a reorder has
no visible affordance at all — the row you are dragging looks identical to
every other row. There is no way to reach the flag from Swift to supply your own
treatment.
Where
Sources/SkipUI/SkipUI/Containers/List.swift, RenderReorderableItem:
@Composable private func RenderReorderableItem(reorderableState: ReorderableLazyListState, key: String, modifier: Modifier, content: @Composable (Modifier) -> Void) {
ReorderableItem(state: reorderableState, key: key, defaultDraggingModifier: modifier) { dragging in
var itemModifier = Modifier.detectReorderAfterLongPress(reorderableState)
if dragging {
let elevation = animateDpAsState(8.dp)
itemModifier = itemModifier.shadow(elevation.value)
}
content(itemModifier)
}
}
dragging is known here and discarded. A shadow communicates elevation by
darkening what is around the row, which does nothing over #000/#141414.
Material's own answer on dark surfaces is tonal elevation — the surface gets
lighter — which a shadow cannot express.
Why an app cannot work around it
- The flag never reaches Swift.
onMoveis invoked fromonDragEnd
(rememberReorderableLazyListState(..., onDragEnd:)→commitMove()), so
SwiftUI's.onMovefires on drop. During the drag the app knows nothing. - A gesture of one's own does not fire.
Modifier.detectReorderAfterLongPress
claims the long press first, so.onLongPressGesture(..., onPressingChanged:)
on the row never sees the press. Verified on device. - And that callback is one-way regardless.
onLongPressChange()calls
onChanged(true); nothing ever calls it withfalse
(System/Gesture.swift), so a press-derived highlight would latch on rather
than follow the finger.
Suggested directions
- Apply a tonal treatment as well as the shadow when dragging — e.g. compose the
row over a surface tinted by elevation, so dark themes get feedback that a
shadow cannot give them. - Or make it configurable, in the shape
material3Ripplealready uses: an
environment hook letting the app supply the dragging modifier. - Or expose
draggingto the row content so the app can style it, which is what
SwiftUI apps do on iOS anyway.
Any one of these turns a currently-invisible interaction into a visible one. (1)
alone would fix it for every dark-themed Skip app without an API change.
Environment
skip-ui as vendored in a Skip app, Android 14, Samsung SM-A326U. Reproduces with
any List using .onMove on a dark background.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in Sources/SkipUI/SkipUI/Containers/List.swift at RenderReorderableItem and trace how dragging is supplied by ReorderableItem and how onDragEnd commits the move. Review System/Gesture.swift for the documented gesture callback behavior, then reproduce List reordering on a dark background. Done means the active row has a visible drag affordance without changing drop behavior, with the chosen treatment or API direction clearly settled.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, swift
- Domain
- design, mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100