Skip to content

desktop_drop: add typed async drops and iOS support over shared FFI - #523

Merged
boyan01 merged 8 commits into
mainfrom
desktop-drop-synchronous-decisions
Oct 16, 2026
Merged

boyan01 merged 8 commits into
mainfrom
desktop-drop-synchronous-decisions

Conversation

@boyan01

@boyan01 boyan01 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Let each DropTarget choose reject/copy/move/link through Flutter hit testing, then consume typed file, directory, and string items in an asynchronous onDrop callback. Provider resources remain available until that callback completes, including when its widget unmounts.

  • Use shared FFI for all native decisions, events, and reads; no MethodChannel is registered. Keep synchronous Windows/macOS/iOS acceptance, asynchronous GTK/Android queries, and direct Dart objects on Web. Shared C/C++ code lives in src/.
  • Add iOS 13+ support and an iOS example host. macOS and iOS share Darwin transport, type helpers, and resource cleanup through sharedDarwinSource. UIKit transfers start inside performDrop and retain temporary copies through onDrop; macOS promises are received once, on demand.
  • Expose DropFileItem, DropDirectoryItem, and DropStringItem. Reuse XFile/XDirectory for reading and directory access. Preserve source MIME types and infer missing file types from names without reading content. URLs and HTML remain strings; the plugin does not download them.
  • Cache hover offers and send compact updates. Keep final target selection, operation validation, generation checks, and cache-miss recovery. On Windows, acquire synchronous virtual-file media before native Drop returns; asynchronous sources retain their completion protocol. Preserve source drag previews.
  • Release native readers, permissions, and temporary resources after onDrop. Remove unused directory-enumeration and memory-file paths, unused bindings, and redundant Map identity state. Centralize Linux completion and teardown without finishing hover-only sessions.
  • Update the playground to read content during onDrop and display the retained result. Child rules use MIME types, and overlapping children demonstrate hit testing and parent fallback.
+----------------------------------+-----------------------------+
| Parent: rule / enabled           | Received items | Content    |
|   +-------------------+          |                | Image/text |
|   | Child A: TXT      |          |                | Hex/details|
|   |       +-------------------+  |                |            |
|   +-------| Child B: PNG      |  |                |            |
|           +-------------------+  |                |            |
| Action: reject/copy/move/link     | Saved bookmark access       |
| Swap layers                      |                             |
+----------------------------------+-----------------------------+

Closes #2, closes #68, closes #174, closes #243, closes #305, closes #389, closes #502, closes #196, closes #219, closes #260, closes #339, closes #368, closes #506.

Related reports that remain open: #315 (URL-only browser image drags are delivered as strings rather than downloaded files), #421 (the reported production TypeError still needs reproduction), #386, #299, #346, #373, #394, and #382. These are not claimed fixed by this PR.

Evidence

  • Before: Per-widget decisions were disconnected from native acceptance, and file-oriented delivery did not express string representations, operations, or asynchronous provider lifetime consistently.
    After: The shared selection path chooses a target and operation, delivers typed items, awaits onDrop, and releases provider resources. Compact hover updates reuse the cached offer.
  • Earlier working revisions built on macOS with SwiftPM and CocoaPods, iOS Simulator, Windows, Linux, and Android. These builds predate the final cleanup and do not validate the final commit. The maintainer also exercised macOS, Windows, and WSLg drops before the latest refactors.
  • For commit 6c339da, regenerated Dart FFI bindings and the standalone Darwin header, formatted the changed Dart files, and passed git diff --check. The running Web example hot-reloaded after the Dart cleanup; that does not cover native-only code.
  • Automated tests and native builds were not rerun for the final cleanup, at the maintainer's request. Existing tests were updated to the current API and provider protocols. iOS device drag/drop and the remaining example README scenarios still need manual validation. Older CI results apply only to their tested commits.

Merge Danger

Door: two-way

Source changes can be reverted. No release, version bump, or data migration is included.

Blast Radius: desktop_drop

Breaking API: required onDropDecision replaces onWillAccept, async onDrop replaces onDragDone, and typed DropItem representations expose XFile/XDirectory instead of the former file wrappers. Provider-backed content must be consumed during onDrop; event disposal and byte-backed constructors are removed. URL-only image representations are no longer automatically downloaded. The minimum SDKs are Flutter 3.47.7 / Dart 3.13.5. Native acknowledgement can precede application completion on platforms whose source protocol cannot defer it; iOS and Android offer copy only.

Select destinations through Flutter hit testing and check acceptance again before delivery. Use a shared FFI bridge for synchronous macOS and Windows decisions, asynchronous GTK queries, and synchronous browser feedback.

Preserve platform file handling and add independent example destinations with an inspector. Require onWillAccept and Flutter 3.47.7 / Dart 3.13.5; release metadata remains unchanged.
Remove cross_file and keep metadata plus read/save methods on DropItem. Preserve original browser Blobs and stream bounded slices instead of re-fetching object URLs.

Materialize accepted image data/src URLs with browser CORS handling, capture drag events before descendant bubbling handlers, and document secondary-engine registration and remaining host-specific regression cases.

Refs #315, #339, #506, #196, #421, #219, #368, #260. Local validation was limited to formatting and static analysis; runtime regression remains pending.
Avoid fetching an already available data URL and align the browser regression with the widget test event loop. Clamp file read ranges at EOF consistently with native streams.
Delegate filesystem and retained browser Blob reads to XFile while preserving synchronous drag metadata, byte-backed constructors, empty ranges, and saveTo. Replace fixed browser chunk assertions with content checks and document the updated dependency.
Forward accepted and rejected OLE drag events to IDropTargetHelper using screen coordinates and the existing target decision. Clear the helper on leave, drop, and shutdown while keeping drag acceptance independent of optional Shell image support.
Expose typed file, directory, and string items with per-target operation decisions. Scope provider resources to asynchronous onDrop callbacks, preserve view-level bridges, and cache hover metadata.

Use one shared native FFI transport in src and shared Darwin code for macOS and iOS. Preserve lazy promise reception, capture synchronous Windows virtual content, and delegate filesystem reads to XFile and XDirectory.

Update the example to consume content during drop, normalize missing file MIME types, remove unused protocols and bindings, and centralize Linux cleanup.
@boyan01 boyan01 changed the title desktop_drop: add per-target acceptance for files, text, and URLs desktop_drop: add typed async drops and iOS support over shared FFI Oct 16, 2026
UIKit supplies readable item providers at release even when drag item identities are unchanged. Refresh the cached offer before accepting the drop so files from Photos and Files can be loaded.

Add an iOS regression test that replaces the provider between hover and drop. Verified the test fails before the fix and passes afterward; manually verified Photos and Files drops on the iPad simulator.
@boyan01
boyan01 merged commit e74c1d3 into main Oct 16, 2026
9 checks passed
@boyan01
boyan01 deleted the desktop-drop-synchronous-decisions branch October 16, 2026 08:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment