Uploader

Pub Package

This package is used for creating AAB/APK and IPA files, and sending them to Play Console, TestFlight, and Firebase App Distribution

Installation

IOS

  • Create a JSON file with issuer_id and auth_key, and provide the path of the JSON file to the Uploader's testFlightConfig->path parameter.
  • issuer_id can be obtained in Users and Access under App Store Connect.
  • For auth_key, create a new Developer Key in the same section as issuer_id. The id value of this key can be used as the auth_key. Download the created key file and place it in the ~/private_keys/ directory on the computer where the package will be called.

instruction

ANDROID

FIREBASE APP DISTRIBUTION

  • Install Firebase CLI (https://firebase.google.com/docs/cli?hl=tr).
  • Activate App Distribution on Firebase .
  • Under Settings in Firebase , integrate with Google Play in the Integrations tab. If the app has not been published yet, this integration cannot be done, and in this case, Uploader 's androidBuildType parameter should be set to apk .

Usage

dev_dependencies:
  uploader: any
dart run uploader              # asks for confirmation before it starts
dart run uploader --yes        # no prompt, for CI
dart run uploader --dry-run    # prints every command without running any
dart run uploader --help

The process exits with a non-zero status when anything fails, so it can be used as a CI step directly.

Configuration

uploader:
  platform: all # ios, android, all
  uploadType: all # appDistribution, store, all
  testFlightConfig:
    path: ios_deploy_config.json # must include auth_key and issuer_id
  playStoreConfig:
    path: android_deploy_config.json # must include client_email, client_id, private_key
    track: internal # internal, alpha, beta
    releaseStatus: completed # completed, draft. default: completed
    skslPath: null
  appDistributionConfig:
    androidBuildType: abb # abb, apk
    androidTesters:
      path: android/testers.txt
    iosTesters:
      url: https://testers.txt
    androidGroups: # Firebase App Distribution group aliases
      - qa
    iosGroups:
      - qa
    releaseNotesPath: release_notes.txt
  useParallelUpload: true # default: true
  uploadRetryCount: 2 # extra attempts per upload step. default: 2
  enableLogFileCreation: false # default: false
  extraBuildParameters: null

Build parameters

extraBuildParameters is appended to every flutter build the run performs. Two optional lists narrow that down to a single target:

uploader:
  extraBuildParameters: # every build
    - --no-tree-shake-icons
  appDistributionExtraBuildParameters: # App Distribution builds only
    - --dart-define=APP_CHANNEL=staging
  storeExtraBuildParameters: [] # store builds only

A build receives the common list followed by its own target's list. Writing none of these keys behaves exactly as before.

This works because a run builds each target separately, so the two never share an artifact:

App Distribution build Store build
iOS flutter build ipa --export-method=ad-hoc flutter build ipa --export-method=app-store
Android, androidBuildType: apk flutter build apk flutter build appbundle
Android, androidBuildType: abb flutter build appbundle flutter build appbundle

With uploadType: all in abb mode that means two app bundles are produced. The cost is one extra build; the gain is that a diagnostic --dart-define meant for testers never reaches the binary that goes to the store.

The confirmation screen prints the resolved list for each target before anything is built, so a flag written to the wrong list is visible up front.

Production releases are deliberately out of scope: the tool publishes to the test tracks and the production rollout stays a Play Console decision.

Only platform and uploadType are required. Every other key falls back to the default shown above.

iOS size check

After each IPA is built the run prints how large it is and compares that to the App Store's 200 MB over-the-air download limit:

[INFO]
[ios] IPA payload is 177.9 MB, 22.1 MB under the 200 MB App Store limit

Within 15 MB of the limit the line becomes a WARNING, and over the limit an ERROR. Both of those also list the fifteen largest items in the archive, so a build that grew says what grew:

[ERROR]
[ios] IPA payload is 203.8 MB, 3.8 MB over the 200 MB App Store limit
     51.5 MB  Runner
     21.0 MB  Frameworks/media_kit_libs_ios_video.framework
     15.3 MB  Frameworks/App.framework
     10.1 MB  Frameworks/App.framework/flutter_assets/assets/images
     ...

The number is the uncompressed total of everything under Payload/, in decimal MB. An IPA also carries Signatures/ and Symbols/, which are not installed on a device — counting the whole archive reads far larger than the app actually is.

Items are grouped at frameworks and resource bundles, because that is the unit that gets added or removed. Flutter's assets are reported separately from App.framework even though they ship inside it; otherwise an asset that grew would look like the Dart snapshot growing.

unzip closes its listing with a totals row, and the run checks the entries it read against it. That matters because the figure can only ever be too low: if a line were skipped, an oversized build would otherwise be reported as a comfortable one. When the two do not agree the size is presented as a lower bound and the limit is reported as unresolved rather than cleared — an oversized build still reads as oversized either way.

The check never stops a run. Whether an oversized build is still worth sending to testers is a decision for whoever reads the warning, and failing here would only strand an artifact that was built successfully.

It needs unzip on the PATH. With no archive on disk nothing is printed, so --dry-run is unaffected; if the archive is there but cannot be read, the run says the size was not checked instead of staying quiet.

Collaborators

Libraries