Solving Drag-and-Drop in Flutter macOS: A Journey

The Problem

Implementing native drag-and-drop functionality in a Flutter macOS application that works seamlessly with scrolling and file selection proved to be one of the most challenging features we’ve built. The goal was simple: allow users to drag files into and out of CodeFrog, supporting both local and network (SSH) projects, while maintaining smooth scrolling and file selection.

Initial Attempt: super_drag_and_drop

We started with the super_drag_and_drop package, which provides cross-platform drag-and-drop support. While it worked for basic scenarios, we encountered several issues:

  1. Unreliable drag-out for remote files – Files on network/SSH connections needed to be downloaded before dragging, and the async nature of this operation caused frequent failures
  2. Gesture conflicts – The package’s gesture recognizers conflicted with Flutter’s built-in scrolling and selection mechanisms
  3. Limited control – We needed more fine-grained control over the drag-and-drop behavior, especially for handling remote file downloads

After many hours of debugging and attempting workarounds (pre-downloading files, using virtual files, adjusting gesture recognizers, etc.), we decided to build a custom solution using native macOS APIs.

The Custom Solution: flutter_macos_drag

We built a custom Flutter plugin (flutter_macos_drag) that uses native macOS NSDraggingSource and NSDraggingDestination protocols directly. This gave us complete control over the drag-and-drop behavior.

Key Components

  1. MacOSDraggable – A Flutter widget for dragging files out of the app
  2. MacOSDroppable – A Flutter widget for accepting file drops from Finder
  3. DraggableNSView – Native Swift view implementing NSDraggingSource and NSDraggingDestination

The Challenge: Scrolling vs Drag-and-Drop

The biggest challenge was making drag-and-drop work while preserving scrolling functionality. The native AppKitView needed to be in the widget hierarchy to receive drag events, but it was blocking pointer events needed for scrolling.

Failed Approaches:

  • Using IgnorePointer – Blocked drag events
  • Using AbsorbPointer – No effect
  • Using Listener with HitTestBehavior – Still blocked events
  • Reversing Stack order – Drag events didn’t reach the native view

The Solution: hitTest Override

The breakthrough came from understanding how macOS handles drag events vs pointer events:

  1. Drag events work at the NSView level and query all registered views directly, completely bypassing Flutter’s pointer system and hit testing
  2. Pointer events (scrolling, clicking) go through normal hit testing

By overriding hitTest in the native view to return nil for drop zones, we allow pointer events to pass through to Flutter widgets below, while drag events still work because they query registered views directly.

override func hitTest(_ point: NSPoint) -> NSView? {
    // For drop zones, return nil to let pointer events pass through to Flutter
    // Drag events don't use hitTest - they query all registered views directly
    if acceptDrops && filePath == nil {
        return nil
    }
    return super.hitTest(point)
}

Additionally, we made mouse event handlers return early for drop zones:

override func mouseDown(with event: NSEvent) {
    // For drop zones, don't handle mouse events - let them pass through for scrolling
    if acceptDrops && filePath == nil {
        return // Don't call super - allows events to pass through
    }
    // ... handle drag-out logic
}

Widget Structure

The final widget structure uses a Stack with the native view on top:

Stack(
  children: [
    // Native view on top - configured to not block pointer events
    Positioned.fill(
      child: Opacity(
        opacity: 0.01,
        child: AppKitView(...),
      ),
    ),
    // Flutter widgets below - receive pointer events for scrolling
    widget.child,
  ],
)

Features Achieved

Drag files out – Works for both local and network files
Drag files in – Accepts drops from Finder into any directory
Scrolling – File tree pane scrolls smoothly
File selection – Click to select files works normally
Remote file handling – Downloads remote files on-demand during drag
Root directory support – Can drop files at project root (empty path)

Key Technical Insights

  1. macOS drag events bypass Flutter’s pointer system – They query all registered views directly, so IgnorePointer and similar widgets don’t affect them
  2. hitTest controls pointer events, not drag events – Returning nil from hitTest allows pointer events to pass through while drag events still work
  3. View registration is separate from hit testing – Views registered with registerForDraggedTypes receive drag events regardless of hitTest results
  4. Mouse event handlers must return early – For drop zones, don’t call super in mouse event handlers to allow events to pass through

Lessons Learned

  • Sometimes a custom native solution is necessary when cross-platform packages don’t meet specific requirements
  • Understanding the underlying platform APIs (NSDraggingSource/NSDraggingDestination) is crucial
  • The interaction between Flutter’s pointer system and native platform views requires careful consideration
  • Persistence pays off – this took many hours but resulted in a robust, maintainable solution

This solution was developed over many hours of debugging and research. The key was understanding that macOS drag events operate at a different level than pointer events, allowing us to let pointer events pass through while still receiving drag events.

CodeFrog beta available for testing

CodeFrog for Mac Desktop now available for open testing. It’s a tool for developers.

https://testflight.apple.com/join/xz2v1wYq

I’d love it if you take a look if you’re a dev. Accessibility testing, security testing, bulk domain security testing via DNS API, analyze code with static analysis tools, find vulnerabilities with OSV, HTML validation, GitHub PR response automation, connect to servers via ssh and view your cpu, ram, hd usage.

I have a decision to make

Why can’t sandboxed mac app store apps have full disk access available in the system settings for full disk access?

I discovered mac app store apps in release mode cannot access the ai auggie command line program and other command line programs like opengrep on your system. Debug builds fine.

I came up with a workaround: Since I have an ssh client built in for connecting to remote servers, why not connect to ssh on the same local machine… Ask the user for their username and password in a popup.

To do this, you have to enable remote login on your mac in system settings -> sharing.

In addition you must grant full disk access to cli ssh in system settings:  add /usr/libexec/sshd-keygen-wrapper

It all works, but I don’t see the cli program in mac settings. To remove the cli program you must run a command line program to remove all full disk access support from all apps. No way to just undo ssh.

So my question is, even though I got CodeFrog all working for a mac app store release, should I not do it because it’s insecure or too complicated with the system settings? Should I instead sell the app off the store like Panic Nova?

Need some advice. I have not implemented in app purchases yet. Should I just have a reality check and sell the app off the store, or try for app store approval?

Bummer…

Edit: Maybe I’m ahead of my time, but perhaps Apple could review the source code for apps requesting full disk access and make sure there’s nothing fraudulent in them. Then, developer tools app store apps could be in the store with the user’s assurance that nothing is happening behind the scenes that is scary.

Edit2: I asked my question in Apple dev forums.

https://developer.apple.com/forums/thread/807142

CodeFrog uploaded to Testflight

Uploaded my first build of CodeFrog to Testflight today! It is waiting for review.

CodeFrog is a developer tool coming soon for macOS. It helps automate security testing, speeds up pull request reviews by exporting all CodeRabbit comments to an AugmentCode task list, and more.

I still have to build the website for it and add payments and help docs, but the app is mostly done.

CodeFrog: Find & Fix Bugs Fast with macOS and Mobile Apps – Vibe & Verify

CodeFrog is a professional, Flutter‑based mobile and desktop app that brings modern development workflows to your pocket. With API integrations from GitHub, Linode, Hetzner, and Sendgrid, secure SSH server management, GitHub PR review integrations, and a powerful Web Testing and Security Scanning toolkit, CodeFrog helps you diagnose, fix, and ship software faster—from anywhere. You can develop on your phone while connected to a remote server like mac.

CodeFrog is currently in development. I hope to have a macOS release before the end of the year, with mobile apps following soon after.

Ethical and legal use notice: Only run security scans against systems you own or are explicitly authorized to test. Unauthorized scanning may violate laws and terms of service.


Security Scanning Features

CodeFrog’s security scanners focus on high‑signal, read‑only checks inspired by OWASP guidance and industry best practices.

  • Single‑site security scanner with OWASP‑based checks
  • Bulk security scanner connected to Linode API for DNS with concurrent scanning of multiple targets
  • HTTPS/HTTP automatic fallback with clear indicators
  • Real‑time streaming results as scans complete
  • Critical findings popup alerts
  • Severity‑based filtering (Critical, High, Medium, Low, Info)
  • Live findings display while scanning is in progress
  • HTTP fallback indicators throughout the UI
  • Selectable/copyable URLs and findings
  • “Open in browser” buttons for quick access

What this means in practice:

  • CodeFrog performs safe, read‑only HTTP methods (HEAD/GET/OPTIONS)
  • Results stream in as each target finishes—no need to wait for the entire batch
  • If HTTPS fails, CodeFrog retries over HTTP and clearly marks any fallback usage
  • Critical results trigger a blocking alert so you can prioritize remediation immediately

Export & share:

  • Export findings as JSON/Markdown/CSV
  • Copy‑to‑clipboard for single findings or entire result sets
  • Summary chips by severity filtering in bulk mode

Web Testing & Code Analysis Features

A practical suite for validating web experiences and inspecting network characteristics—all in one place.

  • HTML validation

  • Meta tags analysis

  • Page timing metrics

  • Size analysis

  • Accessibility scanning

  • Open Source Vulnerability scanning (OSV.dev)

  • Static analysis with OpenGrep/Semgrep (macOS desktop)

  • Line counting with configurable exclusions (e.g., *.log files)

  • Secrets scanning with Gitleaks (MIT licensed, install with Homebrew)

  • Exclusion of third‑party directories (e.g., Pods) from secrets scans

  • Exclusion of Flutter build artifacts (.dart_tool, build) from scans

  • Selectable/copyable validator results with “Copy All Errors” action

  • Display of zero‑count severities in bulk scanner

  • “Scan first N unscanned” functionality for incremental scanning

Highlights:

  • Accurate timing breakdown (DNS, TCP connect, TLS handshake, TTFB, download)
  • Resource inventory with compressed/uncompressed size insights
  • Meta tags validator for Open Graph and Twitter Card with quick previews

GitHub Integration

Turn PR feedback into action with lightweight, mobile‑friendly workflows.

  • PR comments viewer with CodeRabbit integration
  • Easy export of GitHub PR comments into Augment Code markdown task list format
  • Task titles prefixed with PR number and comment number (e.g., “PR#45 Comment #123: task title”)
  • Bulk selection and delete/re‑import functionality for GitHub tasks
  • Filtering for unresolved comments only
  • Hide/expand first comment by default
  • Import options (first/5/10/all comments)
  • Structured AI suggestion parsing with title and description sections
  • Export/post all remaining raw comment texts when lacking AI summary

Benefits:

  • Create actionable tasks from PR comments in seconds
  • Keep review context portable across devices and sessions
  • Maintain signal by filtering unresolved items and hiding nitpicks by default

Servers Screen Features

Manage and monitor your development infrastructure securely.

  • At‑a‑glance server statistics dashboard showing all servers simultaneously
  • Real‑time disk space monitoring with cron job setup
  • SSH connection pooling (one login per unique server)
  • Server management with RSA 4096‑bit SSH keys
  • Secure private key storage via Flutter Secure Storage

Why it matters:

  • Strong, key‑based authentication by default (RSA‑4096)
  • Minimal re‑authentication thanks to connection pooling
  • Early warning on low‑disk conditions—right inside the app

API Integrations & Automation Value

CodeFrog connects to your ecosystem to eliminate manual tasks and coordinate workflows.

Hetzner API

  • SSH key management
  • Server statistics retrieval

Linode API

  • Automated domain discovery for bulk security scanning
  • DNS record enumeration
  • Website target generation from domain records

SendGrid API

  • Disk space notification service (low-disk alerts via email)

GitHub API

  • PR comments retrieval and parsing
  • Issue/comment status management (resolve/unresolve)
  • Task import automation
  • Integration with Augment Code workflow

Each integration replaces multi‑tool manual steps with streamlined, in‑app actions. The result: fewer context switches and faster cycles from feedback to fix.


Architecture and Privacy at a Glance

  • Flutter + Dart with Riverpod for reactive state
  • Read‑only scanners; no active exploitation or credential use
  • Hybrid data model: local SQLite (drift) for session data; secure storage for secrets
  • HTTPS/HTTP fallback clearly indicated; TLS verification on by default
  • Accessibility and WCAG AA contrast targets across the UI

Getting Started

  1. Open CodeFrog and navigate to Web Testing → Security Scan
  2. Enter a URL or switch to Bulk mode to scan multiple targets
  3. Watch live results stream in; use filters and “Open in browser” to inspect quickly
  4. Export findings and copy summaries directly to issues or tasks

For GitHub PR workflows, open the PR viewer to import comments as tasks, filter unresolved discussions, and keep your reviews moving—wherever you are.


A safer, faster path from feedback to fix

With practical, read‑only checks and tight integrations, CodeFrog turns security scanning and web diagnostics into a fast, mobile‑ready workflow. Combine it with GitHub PR tooling and server management to close the loop—from detection to resolution—without leaving your device.

I created a React Native macOS project for using Executorch on device AI

There was a project for Android and iOS but not macOS, so I open sourced my work getting Executorch working in my upcoming app AlienTavern.

Check it out:

https://github.com/greenrobotllc/react-native-executorch-macos

I discovered this song I like:

https://open.spotify.com/track/4FcnVk4OypGKh1LJ27ojtj?si=hydpgZh-Qtiu1YOo2PDc_w

I thought I would be upfront about it… On greenrobot.com there was a publicly available sql file available for a very long time. I removed it.

On greenrobot.com there was a publicly available sql file for a very long time. (not linked anywhere) I removed it. It was from a database backup that for some reason was in public html from I think my time making apps in the facebook app platform days. I have scanned servers manually and also built an automated security scanner and believe there are no more files publicly that shouldn’t be.

I’m sorry. Thanks for websafety.ninja for notifying me there was a vulnerability but not to confirm exactly what it was. It convinced me to scan what was being served and build an automated security scanner for all my domains. This server did not contain any passwords. I used Facebook login for all my apps during that period. I do not collect credit card data. In the future my apps will be more secure I hope. During that time period I developed apps more on the server than locally due to FBML feature from Facebook. I now have switched to local machines for dev and utilize code review from CodeRabbit going forward for most stuff.

Edit: Thinking back, I can’t remember ever putting a file in the web root like that file. I have complained previously about my phone being hacked with a Pegasus like virus that sent horrendous and scary texts I didn’t write and a mac virus that popped up a White House tweet while I was viewing Donald Trump’s tweets. I’d be happy to describe phone hacking more, I have a pinned post on this blog blog.greenrobot.com, but I think it could be that the hackers had access to my computer as well as phone, and in that case they could have had access to my greenrobot.com server as well. If anyone can refer me to someone really technical to talk to about my Pegasus hacking virus please let me know. It’s really disappointing to think about it.

Edit2: I think the file was from a database backup. I didn’t look at the file. I just moved it out of web root.

Edit3: Posted this on LinkedIn, Twitter, Facebook:

Either I messed up and put a sql data file in web root at some point or whoever hacked my phone, mac, or hacked my server and re-added a safety link to greenrobot.com years ago (not me, while I was on a call with ex coworker) did it

I notice I got a photo like from a seymoursayless, then on this photo only I see a less.

I’m not totally sure why this happened but I did delete a tweet and instead send as dm containing an idea I previously told AugmentCode was top secret. The less is not visible on any other of my photos. I am not suggesting AI did it to me. I guess I will calm down and post less stuff online for a bit at least. I showed my Mom this, it’s a little scary.

Update: all instagram photos clicked from notifications have less after the text. Less scared now.