win_drag_source 0.0.2 copy "win_drag_source: ^0.0.2" to clipboard
win_drag_source: ^0.0.2 copied to clipboard

PlatformWindows

Native Windows OLE drag source for Flutter. Drag files or text with a custom RepaintBoundary ghost image, using pure Dart FFI.

win_drag_source #

English · 简体中文

Windows-only Flutter package for native OLE drag-and-drop. Wraps any widget into a drag source with a custom ghost image (no system ghost), supporting both CF_HDROP (file) and CF_UNICODETEXT (text / encrypted payload) modes.

⚠️ Windows-only. On macOS / Linux / Web the wrapper degrades to a plain Listener and never initiates a drag.

Features #

  • ✅ Pure Dart FFI — no native code to compile, no plugin registry headache
  • ✅ Custom ghost image via RepaintBoundary — exclude badges, frames, captions from the drag preview via imageKey
  • ✅ Two payload modes: file path or arbitrary text (typically encrypted)
  • ✅ Tap / double-tap semantics with Windows-native 500 ms interval
  • ✅ Zero runtime dependencies beyond ffi and win32

Installation #

dependencies:
  win_drag_source: ^0.0.1

Then:

flutter pub get

Quick start #

1. File drag (CF_HDROP) #

The simplest case — drag a real file into Explorer, 3ds Max, SketchUp, chat apps, anything that accepts files:

import 'package:win_drag_source/win_drag_source.dart';

DragSource(
  payload: FilePayload('C:\\path\\to\\file.max'),
  onDropComplete: (accepted) {
    debugPrint('drop ${accepted ? 'accepted' : 'rejected'}');
  },
  child: YourCard(),
)

2. Async payload (DB lookup, decryption, …) #

When the payload needs I/O before the drag can start, use payloadProvider:

DragSource(
  payloadProvider: () async {
    final path = await lookupFilePath(itemId);
    if (path == null) return null;
    return FilePayload(path);
  },
  onDropComplete: (ok) => ...,
  child: YourCard(),
)

If payloadProvider returns null, the drag is silently aborted. The pointer must remain pressed while it resolves; releasing it first also cancels the drag. The asynchronous work itself does not block the UI.

3. Encrypted payload (CF_UNICODETEXT) #

For dragging to a target that knows how to decrypt the payload (e.g. your own renderer / IDE plugin):

DragSource(
  payload: TextPayload(encrypt(yourJson)),
  onDropComplete: (ok) => ...,
  child: YourCard(),
)

The package does not ship an encryption helper — bring your own (package:pointycastle, package:cryptography, …). The package's only job is to deliver the bytes to the OLE drop target.

💡 Plain-text targets (Notepad, chat apps) will also receive the encrypted string and display it as opaque text. Only targets that know your format can decode it.

Custom ghost image #

By default the whole child is rasterized as the drag preview. To drag only the cover image (excluding badges, frames, captions), wrap it in a RepaintBoundary with a GlobalKey and pass the key as imageKey:

final _coverKey = GlobalKey();

@override
Widget build(BuildContext context) {
  return DragSource(
    payload: FilePayload(path),
    imageKey: _coverKey,        // ← rasterize this subtree, not the whole card
    onDropComplete: (ok) => ...,
    child: Stack(
      children: [
        RepaintBoundary(
          key: _coverKey,
          child: Image.file(File(path)),
        ),
        Positioned(right: 8, bottom: 8, child: Badge()),  // excluded from ghost
      ],
    ),
  );
}

API reference #

DragSource #

Param Type Default Description
child Widget The widget to wrap (default ghost image source)
payload DragPayload? Synchronous payload
payloadProvider Future<DragPayload?> Function()? Async resolver; takes precedence over payload
imageKey GlobalKey? Sub-tree to rasterize as ghost image
enabled bool true Master switch
onTap VoidCallback? Single-tap handler
onDoubleTap VoidCallback? Double-tap handler (delays single-tap by 500 ms)
onDropComplete ValueChanged<bool>? Fired after DoDragDrop returns

DragPayload #

Class OLE format Use case
FilePayload CF_HDROP Drop a real file. Receivers: Explorer, 3ds Max, chat apps, …
TextPayload CF_UNICODETEXT Drop an arbitrary string. Receivers: any text field; or your own target that decodes the encrypted payload.

DragDrop (low-level FFI) #

Rarely needed directly — exposed for advanced cases (custom ghost-image pipelines, etc.). Public surface:

Method Description
createDragWindow(width, height) Create a topmost layered window for the ghost image
setDragWindowPixels(hwnd, w, h, pixels) Push BGRA pixels into the window
moveDragWindow(hwnd, x, y) Move the ghost window
showDragWindow(hwnd) Show the ghost window
destroyDragWindow(hwnd) Tear down the ghost window
startFileDrag(path, hwnd, offsetX, offsetY) Run DoDragDrop with CF_HDROP (blocking)
startTextDrag(text, hwnd, offsetX, offsetY) Run DoDragDrop with CF_UNICODETEXT (blocking)
getCursorPosition() Current cursor position in screen coords

How it works #

pointer down ──► move > 4 px ──► resolve payload
                                   │
                                   ▼
                          rasterize RepaintBoundary
                                   │
                                   ▼
                       create Win32 layered window
                                   │
                                   ▼
              DoDragDrop (blocking, runs its own modal loop)
                                   │
                                   ▼
                            destroy window
                                   │
                                   ▼
                          fire onDropComplete

The package does not hide your application window during drag. If you need that behavior (e.g. to drop on a window behind your app), call window_manager's hide() / show() yourself around the drag.

Caveats #

  • Windows-only. On other platforms DragSource is a no-op.
  • Main-thread blocking. DoDragDrop blocks the UI thread by design while its modal message loop runs. payloadProvider may perform async I/O; keep it quick enough that the user can hold the pointer until it resolves.
  • pixelRatio: 1.0. Ghost images are rasterized at device pixel ratio 1.0. On high-DPI displays this may look slightly soft; if you need a crisper image, fork and adjust.

Compatibility #

The package requires Flutter 3.32 or later, Dart 3.8 or later, and Windows. It uses OLE initialization internally, so applications do not need to set up COM before using DragSource. A process that has explicitly initialized the UI thread with an incompatible COM apartment model cannot start an OLE drag.

License #

MIT — see LICENSE.

1
likes
160
points
8
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Native Windows OLE drag source for Flutter. Drag files or text with a custom RepaintBoundary ghost image, using pure Dart FFI.

Repository (GitHub)
View/report issues

Topics

#windows #drag-and-drop #ffi #flutter

License

MIT (license)

Dependencies

ffi, flutter, win32

More

Packages that depend on win_drag_source