Build a Flutter iOS app on Windows or Linux
Develop Flutter on Windows or Linux, prepare a reproducible iOS project, build it remotely with Xcode, and send the signed app to TestFlight.
· 10 min read
Need a Mac for this? NoMac gives your agent full macOS with Xcode for $0.80 an hour, billed per minute. Start now →
You can develop a Flutter app on Windows or Linux and publish its iOS version without owning a Mac. Dart, widgets, tests, Android, web, and desktop targets run locally. The iOS Runner, native plugins, Xcode archive, code signing, and TestFlight release still need macOS.
Keep the complete Flutter application in a reproducible repository, put it on a cloud Mac, and let your agent reconstruct the iOS configuration there before building.
Send the Flutter application root containing both pubspec.yaml and ios/ to the Mac. Sending only the iOS directory omits the Dart packages and Flutter tooling inputs required to create the native build.
What works outside macOS?
| Task | Windows / Linux | Remote Mac |
|---|---|---|
| Write Dart and Flutter widgets | Yes | Can build again |
| Run analysis and Flutter tests | Yes | Optional repeat |
| Run Android, web, or supported desktop target | Yes | Not required |
| Compile the iOS Runner and native plugins | No Xcode | Required |
| Run iOS Simulator | No | Yes |
| Sign and upload to TestFlight | Can request | Required |
Flutter's official iOS setup guide installs Xcode before running on an iOS device or Simulator. Flutter shares a large part of the application, but its iOS output is still a native Apple target.
Prepare a complete Flutter repository
MyApp/
├── pubspec.yaml
├── pubspec.lock
├── .fvmrc # recommended exact Flutter version
├── lib/
├── test/
├── assets/
└── ios/
├── Runner.xcodeproj/
├── Runner.xcworkspace/
├── Runner/
└── Podfile # when the project still uses CocoaPods- ·Commit
pubspec.lockfor an application. - ·Pin an exact Flutter SDK version in
.fvmrcwhen reproducibility matters. - ·Declare every asset in
pubspec.yamland preserve filename case. - ·Commit the complete
ios/Runner project and its bundle settings. - ·Keep generated build directories and local signing data out of Git.
- ·Review plugin versions and their minimum iOS deployment targets.
Flutter is not preinstalled on a NoMac Mac, so the agent installs the pinned version first. Cloning the Flutter SDK into ~/drive, which is kept between sessions, means the next session skips that download. An exact project pin gives the clearest repeatability. The declared Dart and Flutter constraints must agree with the lockfile.
Check the iOS Runner before the first build
Set a unique bundle identifier matching the App Store Connect record. Confirm the product name, deployment target, version, build number, app icon, launch screen, supported orientations, capabilities, and permission descriptions.
Flutter plugins can bring native Swift, Objective-C, CocoaPods, or Swift Package Manager dependencies into the build. A package that works on Android can still require a newer iOS target or an Apple capability.
Run the fast checks locally
flutter pub get flutter analyze flutter test
Also run the app on an Android device, web browser, or supported desktop target when useful. These checks validate Dart and much of the shared interface. They do not prove that the iOS Runner, native plugins, Pods, entitlements, or signing are correct.
Connect Flutter to a cloud Mac
From the directory containing pubspec.yaml, install the CLI and authenticate:
npm install -g @nomac/cli nomac login
Register npx @nomac/cli mcp with your agent. It starts a Mac with start_mac and clones the repository into ~/drive/src, or runs nomac sync . from the same root. On the Mac it runs flutter pub get, which regenerates the iOS build configuration, and builds.
Run flutter analyze and flutter test. Confirm pubspec.lock is committed and the Flutter version is exact. Start a NoMac Mac, put the complete root containing pubspec.yaml and ios/ in ~/drive/src, install the pinned Flutter, then build the iOS target without signing. On failure, distinguish Dart package, Flutter toolchain, plugin, CocoaPods, Xcode, and signing errors.
The Flutter-to-TestFlight loop
1. Compile the iOS target
On the Mac, build for the Simulator without signing. This needs no Apple account, and the agent reads the first actionable error straight from the output.
flutter pub get flutter build ios --simulator
2. Fix the owning layer
- ·Dart solver failures belong to constraints and
pubspec.lock. - ·Missing generated configuration means the Flutter preparation step did not complete.
- ·Plugin deployment-target conflicts need compatible plugin or iOS target choices.
- ·CocoaPods or Swift package failures belong to native dependency configuration.
- ·Bundle ID, capability, and profile mismatches belong to signing setup.
3. Build the signed release
After the compile check passes, the agent calls set_up_signing (CLI: nomac sign). It registers the bundle ID, makes the App Store profiles, and returns the exact Flutter commands for a signed build. Run them, then send the .ipa with send_to_testflight (CLI: nomac testflight). Apple processes it, usually in 5 to 15 minutes, then install it through TestFlight.
4. Test the iOS-specific behavior
Verify startup, text input, safe areas, scrolling, back gestures, dark mode, Dynamic Type, permission dialogs, notifications, deep links, backgrounding, restoration, and every native plugin. Compare the behavior with Android only where the product should genuinely match.
Important limitation: Flutter add-to-app
This guide covers a complete Flutter application with its iOS Runner. Flutter add-to-app modules embedded inside a separate native host need a project-specific integration. A full Mac can build them, but the steps follow the host app: build the Flutter module the way the host expects, then build and sign the host project.
Flutter's current iOS add-to-app documentation also shows that this is a separate integration flow, now moving toward Swift Package Manager for current Flutter releases.
Common mistakes
- ·Sending
ios/without the Flutter application root. - ·Omitting
pubspec.lockor using a floating Flutter channel. - ·Depending on generated local files that a clean build cannot reconstruct.
- ·Assuming an Android pass proves iOS plugin compatibility.
- ·Changing the iOS deployment target to silence one package without checking user impact.
- ·Testing only Flutter's debug behavior and never the signed release build.
Publish the Flutter app
Inspect privacy manifests and data collection for every plugin, prepare screenshots from the processed TestFlight build, and verify the App Store metadata. Run review_lint on the App Store record. Provide review credentials if the app has login. Stage the exact build with publish and require human confirmation before submission.
Follow the complete 2026 publishing guide for the store steps. Windows or Linux remains the Flutter development machine; the cloud Mac performs the Apple-specific part.