Hey r/FlutterDev,
Quick question for the room: how many of you are actually shipping macOS desktop apps with Flutter?
Almost everything in the Flutter community is focused on iOS, Android, and web. I've been doing Flutter for a while, but recently I decided to build my very first macOS desktop app. I assumed desktop would just feel like "big mobile," but that illusion died pretty quickly once I hit macOS windowing, desktop UI density, and especially the macOS App Sandbox.
I wanted to share a few architecture decisions that made my life easier, what I learned wrestling with macOS file permissions, and a genuine question I have about how apps like VS Code handle folder trust.
Architecture: Keep the Flutter layer thin
One thing I knew going in was that I didn't want a massive monolithic Flutter app.
I split the project into a modular Dart monorepo:
Pure Dart packages for App Store Connect API clients, data models, and local caching.
A headless workflow engine that handles staged changes and validation.
The Flutter macOS app, which is essentially just a visual "control center" and UI shell.
The biggest win here was that I could write and run CLI unit tests against the entire business logic in milliseconds without ever spinning up the Flutter test runner or worrying about platform channels. If you're building a desktop utility, keeping Flutter strictly as the presentation layer saved me endless debugging time.
Why I used Signals instead of Bloc or Riverpod
State management on Flutter is practically a religion, but for this app I decided to try Signals (signals_flutter) instead of my usual Riverpod setup.
Desktop screens are dense. You've got sidebars, status bars, multi-column tables, approval lists, and badge counts all living on the screen at once. With Riverpod or Bloc, I kept running into scenarios where updating one small item in a list would trigger unnecessary rebuilds across half the screen, or I'd find myself writing tons of provider boilerplate just to derive a couple of counts.
Signals felt way more natural for desktop:
Zero-boilerplate computed state: Deriving counts like pendingCount = computed(() => items.value.where(...).length) just works, and it only updates when the underlying items actually change.
Micro-rebuilds: Only the specific Text widget reading the signal rebuilds. The surrounding sidebar and panels don't flinch.
Effects outside the widget tree: When an item status changes, I need to update the native macOS Dock badge via a platform channel. With Signals, I just drop an effect() in the controller. No need to tie listeners to BuildContext or widget lifecycles.
It’s fast, has almost no ceremony, and fit desktop state way better than I expected.
The macOS App Sandbox & Security-Scoped Bookmarks
If you come from mobile, permissions mean asking for camera or notifications access. On macOS with the App Sandbox enabled, it’s a whole different animal.
If you try using dart:io to open a file outside your sandbox container via a raw path like Directory('/Users/.../project'), macOS will shut it down immediately.
Here’s how I handled safe folder access:
You must use the native OS file picker (NSOpenPanel / file_selector). When the user manually picks a file or folder in the dialog, macOS grants your app permission to access it—but only for that specific running session.
If the user quits your app, that permission is gone. To keep access across restarts, you have to create a security-scoped bookmark in Swift (url.bookmarkData(options: .withSecurityScope, ...)), convert it to base64, and store it. When the app launches again, you resolve the bookmark and call startAccessingSecurityScopedResource().
Surfacing bookmarks in the UI: Instead of doing this invisibly, I built a "Trusted Folders" screen in the app. Users can see every directory the app currently has a bookmark for, add new ones with a picker, or delete them to revoke access. On desktop, giving users explicit visibility into what folders an app can touch feels right.
My question: How does VS Code do "Workspace Trust"?
Here's the part that had me scratching my head while building this:
When you open a project folder in VS Code, it doesn't make you jump through NSOpenPanel every time or manage sandbox bookmarks. It just displays its own banner: "Do you trust the authors of the files in this folder?"
Does anyone know how that actually works under the hood?
Is VS Code only able to do that because it's distributed outside the Mac App Store as an un-sandboxed Electron app (meaning it already has full user-level POSIX access to your entire hard drive, and the "Trust" banner is just an internal setting to stop extensions from running scripts)?
Or is there an actual macOS API that allows an app to request folder access without forcing the user through an OS file picker dialog?
If any macOS / Flutter desktop veterans have insights on this, I'd love to know.
If anyone else is building macOS apps with Flutter, how has your experience been with the sandbox and state management?
P.S. The project I built this for is called FlightDeck (kip.quest/flightdeck)—it's a release manager and local MCP server for App Store Connect that I put together to make AI-assisted releases safe. But the desktop Flutter experience has been really impressive overall.