Flutter apps

How to build a Flutter app with codemagic.yaml


This guide will illustrate all of the necessary steps to successfully build and publish a Flutter app with Codemagic. It will cover the basic steps such as build versioning, code signing and publishing.

You can find a complete project showcasing these steps in our Sample projects repository.

Adding the app to Codemagic

The apps you have available on Codemagic are listed on the Applications page. Click Add application to add a new app.

  1. If you have more than one team configured in Codemagic, select the team you wish to add the app to.
  2. Connect the repository where the source code is hosted. Detailed instructions that cover some advanced options are available here.
  3. Select the repository from the list of available repositories. Select the appropriate project type.
  4. Click Finish: Add application

Creating codemagic.yaml

codemagic.yaml is a highly customizable configuration file that you can use to build, test and publish Flutter apps, widgets, and Flutter or Dart packages. The Workflow Editor is a quick way to get started building standard Flutter applications.

You can simultaneously set up workflows both in codemagic.yaml and the Workflow Editor. However, when a codemagic.yaml is detected in the repository, it is automatically used for configuring builds that are triggered in response to the events defined in the file and any configuration in the Flutter workflow editor is ignored.

Note: For documentation on building Flutter projects using the workflow editor, please refer to Building Flutter apps via the workflow editor.

In order to use codemagic.yaml for build configuration on Codemagic, it has to be committed to your repository. The name of the file must be codemagic.yaml and it must be located in the root directory of the repository. Detailed explanation can be found here.

Tip You can find codemagic.yaml examples in Codemagic Sample Projects repository.

If you prefer to write your codemagic.yaml file from scratch, you can start with this minimal configuration.

workflows:
    sample-workflow:
        name: Codemagic Sample Workflow
        max_build_duration: 120
        instance_type: mac_mini_m2

Tip You can have more than one workflow in the same codemagic.yaml file. If you are building for both the Android and iOS, simply enter both workflows as:
workflows:
    android-workflow-id:
        name: Android Sample Workflow
        # .......    
        # .......
        # .......  
    ios-workflow-id:
        name: iOS Sample Workflow
        # ......

Scan for the codemagic.yaml file by selecting a branch to scan and clicking the Check for configuration file button at the top of the page. Note that you can have different configuration files in different branches.

Setting the Flutter version

When building a Flutter application with Codemagic, you can customize your build environment by configuring various settings. One such setting is the option to choose the Flutter version or channel for the build process. Codemagic provides several options: defining a specific channel or version, or alternatively, you can leverage Flutter Version Management (FVM) for version management.

workflows:
    sample-workflow:
        environment:
            flutter: stable
Note: The possible versions are stable, beta, and master, along with any specific versions, e.g., 3.7.6. If not specified, the version preinstalled on the machine will be used by default.

If you wish to use Flutter Version Management (FVM) in your Codemagic project, you must define the flutter version as fvm under the environment settings in your workflow.

workflows:
    sample-workflow:
        environment:
            flutter: fvm
Note: This automatically sets the Flutter version from your project’s .fvmrc file (or from .fvm/fvm_config.json if you’re using an older version of FVM). If this file does not exist, the build will fail.

Moreover, when using FVM, Codemagic allows you to set the specific FVM flavor in your codemagic.yaml to provide all the needed flexibility when managing the Flutter version.

workflows:
    sample-workflow:
        environment:
            flutter:
                version: fvm
                flavor: dev
Note: If the requested flavor does not exist in the config file, the build will fail.

Note: When using Melos for managing monorepos in conjuction with FVM, and sdkPath is set to .fvm/flutter_sdk inside melos.yaml, then the sourceFile needs to be linked to the targetFile by running the following command:

ln -s $FLUTTER_ROOT $CM_BUILD_DIR/.fvm/flutter_sdk

Code signing

All applications have to be digitally signed before they are made available to the public to confirm their author and guarantee that the code has not been altered or corrupted since it was signed.

Generating a keystore

You can create a keystore for signing your release builds with the Java Keytool utility by running the following command:

keytool -genkey -v -keystore codemagic.keystore -storetype JKS \
        -keyalg RSA -keysize 2048 -validity 10000 -alias codemagic

Keytool then prompts you to enter your personal details for creating the certificate, as well as provide passwords for the keystore and the key. It then generates the keystore as a file called codemagic.keystore in the directory you’re in. The key is valid for 10,000 days.

Uploading a keystore

  1. Open your Codemagic Team settings, and go to codemagic.yaml settings > Code signing identities.
  2. Open Android keystores tab.
  3. Upload the keystore file by clicking on Choose a file or by dragging it into the indicated frame.
  4. Enter the Keystore password, Key alias and Key password values as indicated.
  5. Enter the keystore Reference name. This is a unique name used to reference the file in codemagic.yaml
  6. Click the Add keystore button to add the keystore.

For each of the added keystore, its common name, issuer, and expiration date are displayed.

Note: The uploaded keystore cannot be downloaded from Codemagic. It is crucial that you independently store a copy of the keystore file as all subsequent builds released to Google Play should be signed with the same keystore.

However, keep the keystore file private and do not check it into a public repository.

Referencing keystores in codemagic.yaml

To tell Codemagic to fetch the uploaded keystores from the Code signing identities section during the build, list the reference of the uploaded keystore under the android_signing field.

Add the following code to the environment section of your codemagic.yaml file:

workflows:
  android-workflow:
    name: Android Workflow
    # ....
    environment:
      android_signing:
        - keystore_reference

Default environment variables are assigned by Codemagic for the values on the build machine:

  • Keystore path: CM_KEYSTORE_PATH
  • Keystore password: CM_KEYSTORE_PASSWORD
  • Key alias: CM_KEY_ALIAS
  • Key alias password: CM_KEY_PASSWORD

Signing Android apps using Gradle

To sign your Android app, simply modify your android/app/build.gradle as follows:

...
  android {
      ...
      defaultConfig { ... }
      signingConfigs {
          release {
              if (System.getenv()["CI"]) { // CI=true is exported by Codemagic
                  storeFile file(System.getenv()["CM_KEYSTORE_PATH"])
                  storePassword System.getenv()["CM_KEYSTORE_PASSWORD"]
                  keyAlias System.getenv()["CM_KEY_ALIAS"]
                  keyPassword System.getenv()["CM_KEY_PASSWORD"]
              } else {
                  keyAlias keystoreProperties['keyAlias']
                  keyPassword keystoreProperties['keyPassword']
                  storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
                  storePassword keystoreProperties['storePassword']
              }
          }
      }
      buildTypes {
          release {
              ...
              signingConfig signingConfigs.release
          }
      }
  }
  ...

Creating the App Store Connect API key

Signing iOS applications requires Apple Developer Program membership.

It is recommended to create a dedicated App Store Connect API key for Codemagic in App Store Connect. To do so:

  1. Log in to App Store Connect and navigate to Users and Access > Integrations » App Store Connect API.
  2. Click on the + sign to generate a new API key.
  3. Enter the name for the key and select an access level. We recommend choosing App Manager access rights, read more about Apple Developer Program role permissions here.
  4. Click Generate.
  5. As soon as the key is generated, you can see it added to the list of active keys. Click Download API Key to save the private key for later. Note that the key can only be downloaded once.
Take note of the Issuer ID above the table of active keys as well as the Key ID of the generated key as these will be required when setting up the Apple Developer Portal integration in the Codemagic UI.

Adding the App Store Connect API key to Codemagic

  1. Open your Codemagic Team settings, go to Team integrations > Developer Portal > Manage keys.
  2. Click the Add key button.
  3. Enter the App Store Connect API key name. This is a human readable name for the key that will be used to refer to the key later in application settings.
  4. Enter the Issuer ID and Key ID values.
  5. Click on Choose a .p8 file or drag the file to upload the App Store Connect API key downloaded earlier.
  6. Click Save.

Adding the code signing certificate

Codemagic lets you upload code signing certificates as PKCS#12 archives containing both the certificate and the private key which is needed to use it. When uploading, Codemagic will ask you to provide the certificate password (if the certificate is password-protected) along with a unique Reference name, which can then be used in the codemagic.yaml configuration to fetch the specific file.

  1. Open your Codemagic Team settings, go to codemagic.yaml settings > Code signing identities.
  2. Open iOS certificates tab.
  3. Upload the certificate file by clicking on Choose a .p12 or .pem file or by dragging it into the indicated frame.
  4. Enter the Certificate password and choose a Reference name.
  5. Click Add certificate

If you have added the App Store Connect API key to Codemagic, you can also generate a new Apple Development or Apple Distribution certificate.

  1. Open your Codemagic Team settings, go to codemagic.yaml settings > Code signing identities.
  2. Open iOS certificates tab.
  3. Click Generate certificate.
  4. Provide a Reference name for the certificate.
  5. Choose the Certificate type.
  6. Select the App Store Connect API key to use.
  7. Click Create certificate.

Once the certificate has been created, Codemagic will allow you to download the certificate and provides the password for it.

After downloading, please follow the steps in the Upload certificate tab to upload the certificate to Codemagic.

Note: The certificate can be downloaded only once, right after creating it.

Note: Apple limits the number of Apple Distribution certificates to 3. If you have already reached the maximum number of certificates, the following error will be displayed:

There is a problem with the request entity - You already have a current Distribution certificate or a pending certificate request.

Similar errors can also arise in rarer cases with Apple Development certificates. To resolve the error, either remove some old certificate from the Apple Developer Portal or upload an existing certificate manually.

Existing signing certificates previously generated by Codemagic can be automatically fetched from Apple Developer Portal based on your team’s App Store Connect API key.

Fetching a certificate that was not generated by Codemagic is not possible because each certificate is linked with a private signing key to which Codemagic has no access.

  1. Open your Codemagic Team settings, go to codemagic.yaml settings > Code signing identities.
  2. Open iOS certificates tab.
  3. Click Fetch certificate.
  4. Select a certificate from the Development certificates or Distribution certificates list.
  5. Click Fetch selected.

Adding the provisioning profile

Codemagic allows you to upload a provisioning profile to be used for the application or to fetch a profile from the Apple Developer Portal.

The profile’s type, team, bundle id, and expiration date are displayed for each profile added to Code signing identities. Furthermore, Codemagic will let you know whether a matching code signing certificate is available in Code signing identities (a green checkmark in the Certificate field) or not.

Note: If your app contains app extensions, an additional provisioning profile is required for each extension. Codemagic will use the bundle identifier to find the relevant provisioning profiles. If your bundle identifier is com.example.app, the matching profiles are the ones with com.example.app and com.example.app.* as bundle identifier.

You can upload provisioning profiles with the .mobileprovision extension, providing a unique Reference name is required for each uploaded profile.

  1. Open your Codemagic Team settings, go to codemagic.yaml settings > Code signing identities.
  2. Open iOS provisioning profiles tab.
  3. Upload the provisioning profile file by clicking on Choose a .mobileprovision file or by dragging it into the indicated frame.
  4. Enter the Reference name for the profile.
  5. Click Add profile.

You can automatically fetch the provisioning profiles from the Apple Developer Portal based on your team’s App Store Connect API key. The bundle identifier is listed for every available profile along with it’s name.

The profiles are displayed grouped by category: Development profiles, Ad Hoc profiles, App Store profiles, and Enterprise profiles. For each selected profile, it is necessary to provide a unique Reference name, which can be later used in codemagic.yaml to fetch the profile.

  1. Open your Codemagic Team settings, go to codemagic.yaml settings > Code signing identities.
  2. Open iOS provisioning profiles tab.
  3. Click Fetch profiles
  4. Select the desired profile(s) and enter a Reference name for each one.
  5. Click Download selected. (scroll down if necessary)

Note: When you make essential changes to a provisioning profile, such as modifying the app ID, adding/removing capabilities from the profile identifier, or changing the certificates assigned to that profile, the provisioning profile becomes invalid. In such situations, you need to generate a new provisioning profile with these updates and then re-upload it to Codemagic.

Referencing certificates and profiles in codemagic.yaml

To fetch all uploaded signing files matching a specific distribution type and bundle identifier during the build, define the distribution_type and bundle_identifier fields in your codemagic.yaml configuration. Note that it is necessary to configure both of the fields.

workflows:
  ios-workflow:
    name: iOS Workflow 
    # ....
    environment:
      ios_signing:
        distribution_type: app_store # or: ad_hoc | development | enterprise
        bundle_identifier: com.example.id

Note: If you are publishing to the App Store or you are using TestFlight to distribute your app to test users, set the distribution_type to app_store.

When using a third party app distribution service such as Firebase App Distribution, set the distribution_type to ad_hoc

When defining the bundle identifier com.example.id, Codemagic will fetch any uploaded certificates and profiles matching the extensions as well (e.g. com.example.id.NotificationService).

Using provisioning profiles

To apply the profiles to your project during the build, add the following script before your build scripts:

  scripts:
    # ... your dependencies installation
    
    - name: Set up code signing settings on Xcode project
      script: xcode-project use-profiles
    
    # ... your build commands
See additional configuration options for setting up code signing settings to use given provisioning profiles here

To distribute signed iOS applications solely to internal testers without the need for Apple’s beta review (TestFlight Internal Testing Only):

  
  scripts:  
    # ... your dependencies installation  
    - name: Set up code signing settings on Xcode project  
      script: xcode-project use-profiles   
                --custom-export-options='{"testFlightInternalTestingOnly": true}'  
    # ... your build commands  

Note: Builds marked as TestFlight Internal Only will display “internal” next to the build number and can exclusively be added to internal tester groups. They cannot be submitted for external testing or distributed to customers.

Creating the App Store Connect API key

Signing macOS applications requires Apple Developer Program membership.

It is recommended to create a dedicated App Store Connect API key for Codemagic in App Store Connect. To do so:

  1. Log in to App Store Connect and navigate to Users and Access > Integrations » App Store Connect API.
  2. Click on the + sign to generate a new API key.
  3. Enter the name for the key and select an access level. We recommend choosing App Manager access rights, read more about Apple Developer Program role permissions here.
  4. Click Generate.
  5. As soon as the key is generated, you can see it added to the list of active keys. Click Download API Key to save the private key for later. Note that the key can only be downloaded once.
Take note of the Issuer ID above the table of active keys as well as the Key ID of the generated key as these will be required when setting up the Apple Developer Portal integration in the Codemagic UI.

Automatic vs Manual code signing

Signing macOS apps requires a Signing certificate (App Store development or distribution certificate in .p12 format) and a Provisioning profile. In Manual code signing you save these files as Codemagic Environment variables and manually reference them in the appropriate build steps.

In Automatic code signing, Codemagic takes care of Certificate and Provisioning profile management for you. Based on the certificate private key that you provide, Codemagic will automatically fetch the correct certificate from the App Store or create a new one if necessary.

Certificate types

There are several certificate types you can choose to sign your macOS app, depending on the distribution method you plan to use.

  • MAC_APP_DEVELOPMENT certificate allows you to build your app for internal testing and debugging.
  • MAC_APP_DISTRIBUTION certificate is used to sign a Mac app before submitting it to the Mac App Store
  • MAC_INSTALLER_DISTRIBUTION is used to sign and submit a Mac Installer Package to the Mac App Store
  • DEVELOPER_ID_APPLICATION is used to sign a Mac app before distributing it outside the Mac App Store
  • DEVELOPER_ID_INSTALLER is used to sign a Mac Installer Package before distributing it outside the Mac App Store

For example, in order to publish to Mac App Store, the application must be signed with a Mac App Distribution certificate using a Mac App Store provisioning profile. If you want to create a .pkg Installer package, you must use a Mac Installer Distribution certificate.

Obtaining the certificate private key

To enable Codemagic to automatically fetch or create the correct signing certificate on your behalf, you need to provide the corresponding certificate private key. You then have to save that key as a Codemagic environment variable.

You can create a new 2048 bit RSA key by running the command below in your terminal:

ssh-keygen -t rsa -b 2048 -m PEM -f ~/Desktop/mac_distribution_private_key -q -N ""

This new private key will be used to create a new Mac App Distribution certificate in your Apple Developer Program account if there isn’t one that already matches this private key.

  1. On the Mac which created the Mac App Distribution certificate, open the Keychain Access, located in the Applications and Utilities folder.
  2. Select the appropriate certificate entry.
  3. Right-click on it to select “Export.”
  4. In the export prompt window that appears, make sure the file format is set to Personal Information Exchange (.p12)"**.
  5. Give the file a name such as “MAC_DISTRIBUTION”, choose a location and click Save.
  6. On the next prompt, leave the password empty and click OK.
  7. Use the following openssl command to export the private key:
openssl pkcs12 -in MAC_DISTRIBUTION.p12 -nodes -nocerts | openssl rsa -out mac_distribution_private_key
  1. When prompted for the import password, just press enter. The private key will be written to a file called mac_distribution_private_key in the directory where you ran the command.

Configuring environment variables

  1. Open your Codemagic app settings, and go to the Environment variables tab.

  2. Enter CERTIFICATE_PRIVATE_KEY as the Variable name.

  3. Open the file mac_distribution_private_key with a text editor and copy the entire contents of the file, including the -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY----- tags. Alternatively, you can run the following command on the file:

      cat mac_distribution_private_key | pbcopy

  4. Paste into the Variable value field.

  5. Enter a variable group name, e.g. appstore_credentials. Click the button to create the group.

  6. Make sure the Secure option is selected so that the variable can be protected by encryption.

  7. Click the Add button to add the variable.

  8. Run the following command on the App Store Connect API key file that you downloaded earlier (in our example saved as codemagic_api_key.p8) to copy its content to clipboard:

    cat codemagic_api_key.p8 | pbcopy

  9. Create a new Environment variable APP_STORE_CONNECT_PRIVATE_KEY and paste the value from clipboard.

  10. Create variable APP_STORE_CONNECT_KEY_IDENTIFIER. The value is the Key ID field from App Store Connect > Users and Access > Keys.

  11. Create variable APP_STORE_CONNECT_ISSUER_ID. The value is the Issuer ID field from App Store Connect > Users and Access > Keys.

Tip: Store all the of these variables in the same group so they can be imported to codemagic.yaml workflow at once.

Environment variables have to be added to the workflow either individually or as a group. Modify your codemagic.yaml file by adding the following:

workflows:
  macos-workflow:
    name: macOS Workflow
    environment:
      groups:
        - appstore_credentials

Automatic code signing

To code sign the app, add the following commands in the scripts section of the configuration file, after all the dependencies are installed, right before the build commands.

    scripts:
      - name: Set up keychain to be used for code signing using Codemagic CLI 'keychain' command
        script: keychain initialize
      - name: Fetch signing files
        script: | 
          app-store-connect fetch-signing-files "$BUNDLE_ID" \
            --platform MAC_OS \
            --type MAC_APP_STORE \
            --create
      - name: Fetch Mac Installer Distribution certificates
        script: |  
            app-store-connect certificates list --type MAC_APP_DISTRIBUTION --save || \
            app-store-connect certificates create --type MAC_APP_DISTRIBUTION --save
      - name: Set up signing certificate
        script: keychain add-certificates
      - name: Set up code signing settings on Xcode project
        script: xcode-project use-profiles

Instead of specifying the exact bundle ID, you can use "$(xcode-project detect-bundle-id)".

Based on the specified bundle ID and provisioning profile type, Codemagic will fetch or create the relevant provisioning profile and certificate to code sign the build.

Creating the Installer package

To package your application into an .pkg Installer package and sign it with the Mac Installer Distribution certificate, use the following script:

  scripts:
    - name: Package application
      script: | 
      set -x
    
      # Command to find the path to your generated app, may be different
      APP_NAME=$(find $(pwd) -name "*.app")  
      cd $(dirname "$APP_NAME")
    
      PACKAGE_NAME=$(basename "$APP_NAME" .app).pkg
      xcrun productbuild --component "$APP_NAME" /Applications/ unsigned.pkg  # Create and unsigned package

      # Find the installer certificate common name in keychain
      INSTALLER_CERT_NAME=$(keychain list-certificates \
        | jq '.[]
        | select(.common_name
        | contains("Mac Developer Installer"))
        | .common_name' \
        | xargs)
      
      xcrun productsign --sign "$INSTALLER_CERT_NAME" unsigned.pkg "$PACKAGE_NAME" # Sign the package
    
      rm -f unsigned.pkg                                                       # Optionally remove the not needed unsigned package
Note: Don’t forget to specify the path to your generated package in the artifacts section.

Notarizing macOS applications

Notarization is a process where Apple verifies your application to make sure it has a Developer ID code signature and does not contain malicious content. All apps distributed outside the Mac App Store have to be notarized.

Notarizing an app during the Codemagic build process is possible using the altool command as follows:

xcrun altool --notarize-app -f <file> --primary-bundle-id <bundle_id>
           {-u <username> [-p <password>] | --apiKey <api_key> --apiIssuer <issuer_id>}
           [--asc-provider <name> | --team-id <id> | --asc-public-id <id>]

Normally, you would need to locally sign your app if you are going to publish it to the Microsoft Store. Since this is not possible when using cloud CI/CD, you need to create an MSIX package and publish through Microsoft Partner Center

  1. Follow this guide to setup Microsoft Partner Center authentication and create a new Client secret.
  2. Open your Codemagic app settings, and go to the Environment variables tab.
  3. Enter the desired Variable name, e.g. CLIENT_SECRET.
  4. Enter the API key string as Variable value.
  5. Enter the variable group name, e.g. windows_credentials. Click the button to create the group.
  6. Make sure the Secure option is selected.
  7. Click the Add button to add the variable.
  8. Repeat the steps to also add STORE_ID, TENANT_ID and CLIENT_ID variables.
  9. Add the windows_credentials variable group to the codemagic.yaml:
  environment:
    groups:
      - windows_credentials

Configure scripts to build the app

Add the following scripts to your codemagic.yaml file in order to prepare the build environment and start the actual build process. In this step you can also define the build artifacts you are interested in. These files will be available for download when the build finishes. For more information about artifacts, see here.

  scripts:
    - name: Set up local.properties
      script: | 
        echo "flutter.sdk=$HOME/programs/flutter" > "$CM_BUILD_DIR/android/local.properties"
    - name: Get Flutter packages
      script: | 
        flutter pub get
    - name: Build AAB with Flutter
      script: | 
        flutter build appbundle --release
  artifacts:
    - build/**/outputs/**/*.aab
    - build/**/outputs/**/mapping.txt
    - flutter_drive.log

Note: To build an .apk version for debug, replace the build command with:

  flutter build apk --debug

Note: To build a universal .apk from the existing .aab bundle with user-specified keys, configure the environment variables as explained here and add the following script after the build step:

  scripts:
    - name: Build universal apk
      script: | 
        android-app-bundle build-universal-apk \
          --bundle 'project_directory/build/**/outputs/**/*.aab' \
          --ks /tmp/keystore.keystore \
          --ks-pass $CM_KEYSTORE_PASSWORD \
          --ks-key-alias $CM_KEY_ALIAS \
          --key-pass $CM_KEY_PASSWORD

Please make sure to wrap the --bundle pattern in single quotes. If the --bundle option is not specified, default glob pattern **/*.aab will be used.

  scripts:
    - name: Set up code signing settings on Xcode project
      script: | 
        xcode-project use-profiles
    - name: Get Flutter packages
      script: | 
        flutter pub get
    - name: Install pods
      script: | 
        find . -name "Podfile" -execdir pod install \;
    - name: Flutter build ipa
      script: | 
        flutter build ipa --release \
          --build-name=1.0.0 \
          --export-options-plist=/Users/builder/export_options.plist
  artifacts:
    - build/ios/ipa/*.ipa
    - /tmp/xcodebuild_logs/*.log
    - flutter_drive.log

Note: To build an unsigned .app version for debug, replace the build command with:

  flutter build ios --debug --no-codesign
  scripts:
    
    # ... code signing scripts

    - name: Get Flutter packages
      script: | 
          flutter pub get
    - name: Install pods
      script: | 
        find . -name "Podfile" -execdir pod install \;
    - name: Build Flutter macOS
      script: | 
          flutter config --enable-macos-desktop && \
          flutter build macos --release
    
    # ... create package scripts
        
  artifacts:
    - build/macos/**/*.pkg

Building an unpackaged Windows executable

  scripts:
    - name: Get Flutter packages
      script: | 
          flutter pub get
    - name: Build Flutter Windows
      script: | 
        flutter config --enable-windows-desktop
        flutter build windows --release
        cd build/windows/x64/runner/Release
        7z a -r ../release.zip ./*
  artifacts:
    - build\windows\x64\runner\release.zip
đź”” Note: Flutter changed Windows build path to add the target architecture which landed in 3.15.0-0.0.pre and in stable release v3.16.0. More info can be found here. So, depending on the Flutter version being used, \x64 needs to be added to the path in order to be able to produce a zip file containing .exe

Creating an MSIX package for publishing to Microsoft Store

Codemagic uses the Flutter msix package for packaging the application. For publishing to the Microsoft Store, it is necessary to define certain arguments during packaging.

To pass these arguments to the packaging tool, either add the parameters to the packaging command in codemagic.yaml or add the package to your project and configure the arguments inside the pubspec.yaml file.

To generate MSIX, add the package under dev_dependencies in your pubspec.yaml file:

  dev_dependencies:
    msix: ^2.6.5

Also add the following configuration at the end of the pubspec.yaml file:

  msix_config:
    display_name: <AppName>
    publisher_display_name: <PublisherName>
    identity_name: <PublisherName.AppName>
    publisher: <PublisherID>
    msix_version: 1.0.0.0
    logo_path: ./logo/<file_name.png>
    store: true

The required values are:

  • display_name: The name of your app that will be displayed to users.
  • publisher_display_name: The name of the publisher to be displayed to users (can be an individual’s name or a company’s name).
  • identity_name: The unique identifier of the Windows app.
  • publisher: The Publisher ID present inside your Microsoft Partner Center app.
  • msix_version: Specifies the version of the app’s build. Uses the format “Major.Minor.Build.Revision”, where “Major” cannot be “0”.
  • logo_path: The relative path of the logo file (optional). If not provided, the default Flutter logo is used.
  • store: Setting this to true generates an MSIX package distributable using Microsoft Partner Center

When defining the arguments inside codemagic.yaml, the necessary flags to add to the msix:create command are --store, --display-name, --publisher-display-name, --publisher and --version.

The values for --display-name, --publisher-display-name and --publisher can be found when when logging into Microsoft Partner Center and navigating to Apps and games > Your application > Product Identity.

The argument --display-name should be set to match the value of Package/Identity/Name, the argument --publisher should be set to match the value of Package/Identity/Publisher and the argument --publisher-display-name should be set to match the value of Package/Properties/PublisherDisplayName.

Check out how to version your package in the Microsoft documentation. Note that per Microsoft Store requirements applications are not allowed to have a version with a revision number (last digit of the version) other than zero.

Add the following script after the Build Flutter Windows step:

  scripts:
    - name: Build Flutter Windows
      # ....
    - name: Create package
      script: | 
        # if you did not add the msix pub package to your project:
        flutter pub add msix  
        #
        # if you have specified configuration in pubspec.yaml:
        flutter pub run msix:create
        #
        # if you did not modify `pubspec.yaml`:
        #flutter pub run msix:create --store \
        #  --publisher-display-name=MyName \
        #  --display-name=MyAppName \
        #  --publisher=CN=xx-yy-zz \
        #  --identity-name=com.flutter.MyApp \
        #  --version=1.0.2.0
  artifacts:
    - build/windows/**/*.msix

For all the possible flags for the msix:create command, check the pub documentation. Note that when configuring the flags both in codemagic.yaml and pubspec.yaml, the ones configured in codemagic.yaml take precedence.

  scripts:
    - name: Build web
      script: | 
        flutter config --enable-web
        flutter build web --release
        cd build/web
        7z a -r ../web.zip ./*
  artifacts:
    - build/*.zip

Build versioning

If you are going to publish your app to App Store Connect or Google Play, each uploaded artifact must have a new version satisfying each app store’s requirements. Codemagic allows you to easily automate this process and increment the version numbers for each build. For more information and details, see here.

One very useful method of calculating the code version is to use Codemagic command line tools to get the latest build number from Google Play and increment it by one.

You can find the full sample project with the instructions on alternative ways to perform Android build versioning in our repository.

The prerequisite is a valid Google Cloud Service Account. Please follow these steps:

  1. Go to this link and complete the steps.
  2. You now have a JSON file with the credentials.
  3. Open Codemagic UI and create a new Environment variable GCLOUD_SERVICE_ACCOUNT_CREDENTIALS.
  4. Paste the content of the downloaded JSON file in the Value field, set the group name (e.g. google_play) and make sure the Secure option is checked.
  5. Add the google_play variable group to the codemagic.yaml as well as define the PACKAGE_NAME and the GOOGLE_PLAY_TRACK:
workflows:
  android-workflow-id:
    # ....
    environment:
      groups:
        - keystore_credentials
        - google_play
      vars:
        PACKAGE_NAME: "io.codemagic.fluttersample"
        GOOGLE_PLAY_TRACK: alpha
  1. Modify the build script to fetch the latest build number from Google Play, increment it and pass it as command line argument to the build command
  scripts:
    - name: Build AAB with Flutter
      script: | 
        BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$PACKAGE_NAME" --tracks="$GOOGLE_PLAY_TRACK") + 1))      
        flutter build appbundle --release \
          --build-name=1.0.$BUILD_NUMBER \
          --build-number=$BUILD_NUMBER

In order to get the latest build number from App Store or TestFlight, you will need the App Store credentials as well as the Application Apple ID. This is an automatically generated ID assigned to your app and it can be found under General > App Information > Apple ID under your application in App Store Connect.

  1. Add the Application Apple ID to the codemagic.yaml as a variable
  2. Add the script to get the latest build number using app-store-connect, increment it and pass it as command line argument to the build command:
workflows:
  ios-workflow:
    name: iOS Workflow
    integrations:
      app_store_connect: <App Store Connect API key name>
    environment:
      vars:
        APP_STORE_APPLE_ID: 1555555551
    scripts:
      - name: Flutter build ipa
        script: | 
          BUILD_NUMBER=$(($(app-store-connect get-latest-app-store-build-number "$APP_STORE_APPLE_ID") + 1))
          flutter build ipa --release \
            --build-name=1.0.$BUILD_NUMBER \
            --build-number=$BUILD_NUMBER

Publishing

Codemagic offers a wide array of options for app publishing and the list of partners and integrations is continuously growing. For the most up-to-date information, check the guides in the Configuration > Publishing section of these docs. To get more details on the publishing options presented in this guide, please check the Email publishing, the Google Play Store publishing and the App Store Connect.

Email publishing

If the build finishes successfully, release notes (if passed), and the generated artifacts will be published to the provided email address(es). If the build fails, an email with a link to build logs will be sent.

If you don’t want to receive an email notification on build success or failure, you can set success to false or failure to false accordingly.

workflows:
  sample-workflow-id:
    environment: 
      # ...
    scripts: 
      # ...
    publishing: 
      email:
        recipients:
          - user_1@example.com
          - user_2@example.com
        notify:
          success: true
          failure: false

Publishing to Google Play and App Store

Publishing apps to Google Play requires you to set up a service account in Google Play Console and save the content of the JSON key file to a secure environment variable as explained above in Android Build Versioning steps 1-5. Configuring Google Play publishing is simple as you only need to provide credentials and choose the desired track. If the app is in draft status, please also include the submit_as_draft: true or promote the app status in Google Play.

react-native-android:
  # ... 
  publishing:
    # ...
    google_play:
      credentials: $GCLOUD_SERVICE_ACCOUNT_CREDENTIALS
      track: internal
      submit_as_draft: true

Codemagic enables you to automatically publish your iOS or macOS app to App Store Connect for beta testing with TestFlight or distributing the app to users via App Store. Codemagic uses the App Store Connect API key for authenticating communication with Apple’s services. You can read more about generating an API key from Apple’s documentation page.

Please note that:

  1. for App Store Connect publishing, the provided key needs to have App Manager permission,
  2. and in order to submit your iOS application to App Store Connect, it must be code signed with a distribution certificate.

The following snippet demonstrates how to authenticate with and upload the IPA to App Store Connect, submit the build to beta tester groups in TestFlight and configure releasing the app to App Store. See additional configuration options for App Store Connect publishing here.

Note: Please note that you will need to create an app record in App Store Connect before you can automate publishing with Codemagic. It is recommended to upload the very first version of the app manually. Suppose you have set up an app record but have not manually uploaded the app’s first version. In that case, manual configuration of the settings must be done on App Store Connect after the build is complete, such as uploading the required screenshots and providing the values for the privacy policy URL and application category.
# Integration section is required to make use of the keys stored in 
# Codemagic UI under Apple Developer Portal integration.
integrations:
  app_store_connect: <App Store Connect API key name>

publishing:
  app_store_connect:
    # Use referenced App Store Connect API key to authenticate binary upload
    auth: integration 

    # Configuration related to TestFlight (optional)

    # Optional boolean, defaults to false. Whether or not to submit the uploaded
    # build to TestFlight beta review. Required for distributing to beta groups.
    # Note: This action is performed during post-processing.
    submit_to_testflight: true 

    # Optional boolean, defaults to false. Set to true to automatically expire 
    # previous build in review or waiting for review in Testflight before
    # submitting a new build to beta review. Expired builds will no longer be available for testers.
    # Note: This action is performed during post-processing.
    expire_build_submitted_for_review: true

    # Specify the names of beta tester groups that will get access to the build 
    # once it has passed beta review.
    beta_groups: 
      - group name 1
      - group name 2
    
    # Configuration related to App Store (optional)

    # Optional boolean, defaults to false. Whether or not to submit the uploaded
    # build to App Store review. Note: This action is performed during post-processing.
    submit_to_app_store: true

    # Optional boolean, defaults to false. Set to true to cancel the previous 
    # submission (if applicable) when submitting a new build to App Store review.
    # This allows automatically submitting a new build for review if a previous submission exists.
    # Note: This action is performed during post-processing.
    cancel_previous_submissions: true
    
    # Optional, defaults to MANUAL. Supported values: MANUAL, AFTER_APPROVAL or SCHEDULED
    release_type: SCHEDULED

    # Optional. Timezone-aware ISO8601 timestamp with hour precision when scheduling
    # the release. This can be only used when release type is set to SCHEDULED.
    # It cannot be set to a date in the past.
    earliest_release_date: 2021-12-01T14:00:00+00:00 
    
    # Optional. The name of the person or entity that owns the exclusive rights
    # to your app, preceded by the year the rights were obtained.
    copyright: 2021 Nevercode Ltd
  
    # Optional boolean. Whether or not to release an App Store version update in phases.
    # With this option turned on, your version update will be released over a 7-day period
    # to a percentage of your users (selected at random by their Apple ID) with automatic
    # updates turned on. Learn more from 
    # https://developer.apple.com/help/app-store-connect/update-your-app/release-a-version-update-in-phases.
    # If not specified, then App Store version default phased release configuration is reused.
    phased_release: true

Conclusion

Having followed all of the above steps, you now have a working codemagic.yaml file that allows you to build, code sign, automatically version and publish your project using Codemagic CI/CD. Save your work, commit the changes to the repository, open the app in the Codemagic UI and start the build to see it in action.

Your final codemagic.yaml file should look something like this:

workflows:
  android-workflow:
    name: Android Workflow
    max_build_duration: 120
    environment:
      android_signing:
        - keystore_reference
      groups:
        - google_play
      vars:
        PACKAGE_NAME: "io.codemagic.fluttersample"
        GOOGLE_PLAY_TRACK: alpha
      flutter: stable
    scripts:
      - name: Set up local.properties
        script: | 
          echo "flutter.sdk=$HOME/programs/flutter" > "$CM_BUILD_DIR/android/local.properties"
      - name: Get Flutter packages
        script: | 
          flutter pub get
      - name: Flutter analyze
        script: | 
          flutter analyze
      - name: Flutter unit tests
        script: | 
          flutter test
        ignore_failure: true
      - name: Build AAB with Flutter
        script: | 
          BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$PACKAGE_NAME" --tracks="$GOOGLE_PLAY_TRACK") + 1))      
          flutter build appbundle --release \
            --build-name=1.0.$BUILD_NUMBER \
            --build-number=$BUILD_NUMBER
    artifacts:
      - build/**/outputs/**/*.aab
      - build/**/outputs/**/mapping.txt
      - flutter_drive.log
    publishing:
      email:
        recipients:
          - user_1@example.com
          - user_2@example.com
        notify:
          success: true
          failure: false
      google_play:
        credentials: $GCLOUD_SERVICE_ACCOUNT_CREDENTIALS
        track: $GOOGLE_PLAY_TRACK
        submit_as_draft: true
workflows:
  ios-workflow:
    name: iOS Workflow
    max_build_duration: 120
    integrations:
      app_store_connect: codemagic
    environment:
      ios_signing:
        distribution_type: app_store
        bundle_identifier: io.codemagic.fluttersample
      vars:
        APP_STORE_APPLE_ID: 1555555551
      flutter: stable
    scripts:
      - name: Set up code signing settings on Xcode project
        script: | 
          xcode-project use-profiles
      - name: Get Flutter packages
        script: | 
          flutter pub get
      - name: Install pods
        script: | 
          find . -name "Podfile" -execdir pod install \;
      - name: Flutter analyze
        script: | 
          flutter analyze
      - name: Flutter unit tests
        script: | 
          flutter test
        ignore_failure: true
      - name: Flutter build ipa
        script: | 
          flutter build ipa --release \
            --build-name=1.0.0 \
            --build-number=$(($(app-store-connect get-latest-app-store-build-number "$APP_STORE_APPLE_ID") + 1)) \
            --export-options-plist=/Users/builder/export_options.plist
    artifacts:
      - build/ios/ipa/*.ipa
      - /tmp/xcodebuild_logs/*.log
      - flutter_drive.log
    publishing:
      email:
        recipients:
          - user_1@example.com
          - user_2@example.com
        notify:
          success: true
          failure: false
      app_store_connect:
        auth: integration

        # Configuration related to TestFlight (optional)
        # Note: This action is performed during post-processing.
        submit_to_testflight: true
        beta_groups: # Specify the names of beta tester groups that will get access to the build once it has passed beta review.
          - group name 1
          - group name 2

        # Configuration related to App Store (optional)
        # Note: This action is performed during post-processing.
        submit_to_app_store: false
workflows:
  macos-workflow:
    name: macOS Workflow
    max_build_duration: 120
    environment:
      groups:
        - appstore_credentials
      flutter: stable
      xcode: latest
      cocoapods: default
    scripts:
      - name: Set up keychain to be used for code signing
        script: | 
          keychain initialize
      - name: Fetch signing files
        script: | 
          app-store-connect fetch-signing-files "$BUNDLE_ID" \
            --platform MAC_OS \
            --type MAC_APP_STORE \
            --create
      - name: Fetch Mac Installer Distribution certificates
        script: | 
           # You may omit the first command if you already have
           # the installer certificate and provided the corresponding private key
            app-store-connect certificates list --type MAC_INSTALLER_DISTRIBUTION --save || \
            app-store-connect certificates create --type MAC_INSTALLER_DISTRIBUTION --save
            
      - name: Set up signing certificate
        script: keychain add-certificates
      - name: Set up code signing settings on Xcode project
        script: | 
          xcode-project use-profiles
      - name: Get Flutter packages
        script: | 
          flutter pub get
      - name: Install pods
        script: | 
          find . -name "Podfile" -execdir pod install \;
      - name: Flutter analyze
        script: | 
          flutter analyze
      - name: Flutter unit tests
        script: | 
          flutter test
        ignore_failure: true
      - name: Build Flutter macOS
        script: | 
          flutter config --enable-macos-desktop && \
          flutter build macos --release \
            --build-name=1.0.$PROJECT_BUILD_NUMBER \
            --build-number=$PROJECT_BUILD_NUMBER
      - name: Package application
        script: | 
          set -x
    
          # Command to find the path to your generated app, may be different
          APP_NAME=$(find $(pwd) -name "*.app")  
          cd $(dirname "$APP_NAME")
    
          # Create and unsigned package
          PACKAGE_NAME=$(basename "$APP_NAME" .app).pkg
          xcrun productbuild --component "$APP_NAME" /Applications/ unsigned.pkg

          # Find the installer certificate common name in keychain
          INSTALLER_CERT_NAME=$(keychain list-certificates \
            | jq '.[]
            | select(.common_name
            | contains("Mac Developer Installer"))
            | .common_name' \
            | xargs)
      
          # Sign the package
          xcrun productsign --sign "$INSTALLER_CERT_NAME" unsigned.pkg "$PACKAGE_NAME" 
    
          rm -f unsigned.pkg 
    artifacts:
      - build/macos/**/*.pkg
    publishing:
      email:
        recipients:
          - user_1@example.com
          - user_2@example.com
        notify:
          success: true
          failure: false
      app_store_connect:
        api_key: $APP_STORE_CONNECT_PRIVATE_KEY
        key_id: $APP_STORE_CONNECT_KEY_IDENTIFIER
        issuer_id: $APP_STORE_CONNECT_ISSUER_ID

        # Configuration related to TestFlight (optional)
        # Note: This action is performed during post-processing.
        submit_to_testflight: true
        beta_groups: # Specify the names of beta tester groups that will get access to the build once it has passed beta review.
          - group name 1
          - group name 2

        # Configuration related to App Store (optional)
        # Note: This action is performed during post-processing.
        submit_to_app_store: false
workflows:
  # This workflow is for a normal windows build consisting of the `.exe` file
  windows-workflow:
    name: Windows workflow
    instance_type: windows_x2
    max_build_duration: 60
    environment:
      flutter: master
    cache:
      cache_paths:
        - ~/.pub-cache
    scripts:
      - name: Get Flutter packages
        script: flutter pub get
      - name: Configure for Windows
        script: flutter config --enable-windows-desktop
      - name: Build Windows
        script: flutter build windows
      - name: Export bundle
        script: | 
          cd build/windows/runner/Release
          7z a -r ../release.zip ./*
    artifacts:
      - build/windows/runner/*.zip
    publishing:
      email:
        recipients:
          - email@example.com
  
  # This workflow is for a MSIX package build that can be 
  # published to Microsoft Store using Partner Center
  windows-release-workflow:
    name: Windows release workflow
    instance_type: windows_x2
    max_build_duration: 60
    environment:
      groups:
        - windows-signing
      flutter: master
    cache:
      cache_paths:
        - ~/.pub-cache
    scripts:
      - name: Get Flutter packages
        script: flutter pub get
      - name: Configure for Windows
        script: flutter config --enable-windows-desktop
      - name: Build Windows
        script: flutter build windows
      - name: Package Windows
        script: flutter pub run msix:create
        
        # If you don't have the configurations created inside pubspec.yaml, then you need 
        # to pass the configurations as parameters. Use the following script in that case:
        # ----------------------------------------------------------------------------------
        # - name: Package Windows
        #   script: |
        #     flutter pub add msix
        #     flutter pub run msix:create --display-name='<AppName>' \
        #       --publisher-display-name='<PublisherName>' \
        #       --publisher='<PublisherID>' \
        #       --identity-name='<PublisherName.AppName>' \
        #       --version=1.0.0.0 \
        #       --logo-path='./logo/<file_name.png>' \
        #       --store=true
        # ----------------------------------------------------------------------------------
    artifacts:
      - build/windows/**/*.msix
    publishing:
      partner_center:
        store_id: $STORE_ID
        tenant_id: $TENANT_ID
        client_id: $CLIENT_ID
        client_secret: $CLIENT_SECRET
      email:
        recipients:
          - email@example.com

Next steps

While this basic workflow configuration is incredibly useful, it is certainly not the end of the road and there are numerous advanced actions that Codemagic can help you with.

We encourage you to investigate Running tests with Codemagic to get you started with testing, as well as additional guides such as the one on running tests on Firebase Test Lab or Registering iOS test devices.

Documentation on using codemagic.yaml teaches you to configure additional options such as changing the instance type on which to build, or configuring builds to be automatically triggered on repository events.