How to share a Flutter build with testers (APK and IPA)
· 7 min read
A Flutter project gives you two very different files to hand to testers: an .apk for Android and an .ipa for iPhone. The Android side is mostly a single command. The iOS side depends on how the build is signed, and that is where most "it will not install" messages come from. This guide covers both, from the build command to a link a tester can open on the phone.
What testers can actually install
Before running any command, it helps to know which outputs can be installed from a link and which cannot:
| Output | Command | Installs from a link? |
|---|---|---|
| Universal APK | flutter build apk | Yes, on any supported Android phone |
| Split APKs per ABI | flutter build apk --split-per-abi | Yes, if the tester gets the APK that matches the phone |
| App bundle (.aab) | flutter build appbundle | No, an .aab is for Google Play, not for phones |
| IPA exported Ad Hoc, Development or Enterprise | flutter build ipa --export-method ad-hoc (or development, enterprise) | Yes, on devices the profile allows |
| IPA exported for the App Store | flutter build ipa | No, only through TestFlight or the App Store |
Android: build a test APK
Universal or split?
Running flutter build apk produces one "fat" APK at build/app/outputs/flutter-apk/app-release.apk. It contains native code for every target ABI, so it is larger, but every tester can install the same file. That is usually the right choice for test builds.
Running flutter build apk --split-per-abi produces one smaller APK per ABI in the same folder:
app-armeabi-v7a-release.apkfor older 32 bit ARM phonesapp-arm64-v8a-release.apkfor almost every current phoneapp-x86_64-release.apkfor x86 emulators and a few Chromebooks
Split APKs are worth it when the universal file is large and your testers all use modern phones. In that case share the arm64-v8a file. If you share the wrong split, the phone refuses it with a message that the app is not compatible with the device.
Signing the release APK
A new Flutter project signs the release build type with the debug key until you add your own signing config in android/app/build.gradle.kts (or build.gradle). A debug signed APK installs fine for testing, but it has two side effects. Google Play will not accept it, and a phone that already has the app installed with a different key will refuse the update. Signing test builds with the same upload key you use for releases avoids that later. The details are in Android app signing for APK, AAB and sideloading.
Why not the app bundle?
flutter build appbundle is what you upload to Google Play. Play turns it into device specific APKs. A phone cannot install an .aab directly, so for testing outside Play, always share an APK.
iOS: build an Ad Hoc IPA
An iPhone only installs an app from a link when the build is signed for that device. For a typical test round that means Ad Hoc: the UDID of every test device is listed in the provisioning profile. Development signing works the same way for your own team's devices, and Enterprise signing is for in-house apps of companies in the Apple Developer Enterprise Program.
Step 1: register the test devices
Collect each tester's UDID and add it in the Apple Developer portal under Devices. Testers can read their UDID with the free UDID finder from Safari, without a cable or a Mac. After adding devices, the Ad Hoc profile has to be regenerated so it includes them. The full flow is in iOS Ad Hoc device registration.
Step 2: export with the right method
By default flutter build ipa exports for the App Store. Flutter's documentation lists an --export-method option for other kinds of distribution:
flutter build ipa --export-method ad-hoc
The archive lands in build/ios/archive/ and the exported IPA in build/ios/ipa/.
For repeatable builds, especially in CI, an export options file gives you more control:
flutter build ipa --export-options-plist=ios/ExportOptions.plist
A minimal file looks like this. Since Xcode 15.4 the method names in this file changed: ad-hoc became release-testing and development became debugging. The old names now produce a deprecation warning, so use the new ones if your Xcode is current.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>release-testing</string>
<key>teamID</key>
<string>YOUR_TEAM_ID</string>
</dict>
</plist>
With automatic signing, Xcode picks or creates an Ad Hoc profile that includes the registered devices. With manual signing, add a provisioningProfiles dictionary that maps your bundle id to the name of the Ad Hoc profile.
Step 3: check the IPA before you send it
Most iOS install failures are visible in the file itself. Drop the IPA into the IPA inspector. It reads the file in your browser without uploading it and shows the distribution type, the profile's expiry date and the list of provisioned UDIDs. If the type says App Store, or a tester's UDID is missing, fix that before sharing.
Share the build with a link
With the APK and IPA ready, the sharing part is the same for both platforms on AppForShare:
- Upload the
.apkor.ipaon the home page or in your dashboard. - Copy the install link or show the QR code.
- Testers open the link on the phone and install over the air.
On Android, the tester allows installs from unknown sources for the browser once in the phone settings. On iPhone, the install works only if the device is in the profile, which is why steps 1 to 3 matter.
If you build on every merge, add the upload to your pipeline instead of doing it by hand:
flutter build apk
npx --yes appforshare@1 upload build/app/outputs/flutter-apk/app-release.apk --notes "Checkout screen redesign"
The CLI needs an API token from Dashboard, Settings (Pro and Team plans) in the APPFORSHARE_TOKEN environment variable. More pipeline examples are in uploading builds from CI and the CI docs.
Common Flutter install problems
The iOS app opens and closes right away
You probably shared a debug build. Flutter's documentation notes that on iOS 14 and later, debug mode Flutter apps can only be launched from Flutter tooling, an IDE or Xcode. Launched from the home screen, they quit. Share a release or profile build instead; flutter build ipa builds in release mode.
"This app cannot be installed because its integrity could not be verified"
On iPhone this nearly always means the device is not in the provisioning profile, the profile has expired, or the build was exported for the App Store. Check all three in the IPA inspector. Adding a UDID does nothing for an IPA you already exported: register the device, regenerate the profile, then build and export again. More causes are covered in can't install an app on an iOS device.
Development builds ask for Developer Mode
If you export with the development method, testers on iOS 16 and later need Developer Mode turned on. They find it under Settings, Privacy and Security, Developer Mode, and the phone restarts to apply it. For people outside your team, Ad Hoc is usually the easier route.
"App not installed" on Android
The most common cause is a signature conflict: the phone has a copy of the app signed with a different key, for example a debug build from flutter run, and the new APK is signed with your release key. Android will not update an app across keys. The tester has to uninstall the old copy first. The second cause is a split APK for the wrong ABI; share the universal APK if in doubt.
The upload is refused
If you upload an .aab, it is the wrong format for testers; build an APK. Large universal APKs and IPAs with many frameworks can also pass the file size limit of a plan: 50 MB for guests, 150 MB on Free, 2 GB on Pro and 4 GB on Team. The pricing page lists every limit.
Before each test round
- Bump
versioninpubspec.yaml(the part after+is the build number), so testers can tell builds apart. - Point the build at your test backend, for example with
--dart-definevalues. - Write two or three lines of release notes: what changed and what to test.
- Check the IPA in the inspector and the APK in the APK analyzer, which also flags a debuggable or debug signed build.
Share your next Flutter build
AppForShare gives every .apk and .ipa an install link and a QR code, so Android and iPhone testers install the same way. Builds uploaded to an account also get a store readiness check and a "what changed" comparison with the previous build, which is guidance and not a promise of store approval. The Free plan keeps links for 7 days; Pro at $12 a month adds CI uploads, release notes and links that stay live while you are subscribed. Create a free account and upload your first build.


