MMashDiv

Build and tools reference

Every build flag, Flutter command, tool and script you run on the app's source — apply_app_config.dart and its flags, the release preflight and all its check IDs, the iOS identity phase, the installers, run_tests.sh and the icon generator — with what each one reads, writes and prints.

8 min readUpdated 26 September 2026mobile, reference, flutter, build, dart-define, preflight, apply-app-config, installer, tests, icons

Lookup material. Nothing here is a how-to; for that, see Install and run the app, Build for Android, Build for iOS and Branding.

Run every command on this page from the project root, the folder that holds pubspec.yaml.

Build flags

The app reads exactly one compile-time flag.

Flag Type Default Effect
STORE_RESTRICTED_MODULES bool false true compiles in the Merchant, AI Investment and Affiliate screens. With false their code is left out of the build altogether, not hidden. AI Investment and Affiliate also need the server switch Allow AI Investment & MLM in the Mobile App. See Restricted modules.

Pass it on any build or run command:

flutter build appbundle --release --dart-define=STORE_RESTRICTED_MODULES=true

There is no flag for the backend address, the keys, an environment or a flavour, and the Android project has no product flavours. All of that comes from assets/config/app_config.json, which is packed into the build as an asset. See the configuration reference.

The standard Flutter flags that matter for this app:

Flag Use
--release Every build you give to anyone. A release build of the app refuses a baseUrl that is not https:// and a wsBaseUrl that is not wss://: the build succeeds, and the app opens on its Configuration Error screen naming the key. Check first with dart run tool/apply_app_config.dart --check --release.
--build-name=<x.y.z> --build-number=<n> Override the version: line of pubspec.yaml for this build. The build number must be higher than any you have uploaded.
--obfuscate --split-debug-info=<folder> Optional. Obfuscates the Dart code. Keep the folder for every release, or its crash traces cannot be read.
-d <device id> Choose the phone or emulator for flutter run.

Flutter commands

Command What it does
flutter pub get Fetches the dependencies. Also writes android/local.properties and ios/Flutter/Generated.xcconfig, which the package does not ship and which Gradle and CocoaPods need. Run it first on a fresh copy. The shipped pubspec.lock needs Flutter 3.38 or newer (Dart 3.10 or newer).
cd ios && pod install macOS only. Installs the iOS pods. Run it after flutter pub get, never before. flutter build ios and flutter build ipa run it for you.
flutter run A debug build on the connected phone or emulator. Debug and profile builds accept http:// and ws:// addresses for a local backend.
flutter build apk --release An APK for testing on phones. Output: build/app/outputs/flutter-apk/app-release.apk.
flutter build appbundle --release The App Bundle you upload to Google Play. Output: build/app/outputs/bundle/release/app-release.aab. Without android/key.properties it is signed with the debug key and Play refuses it.
flutter build ipa --release macOS only. An archive and an .ipa for App Store Connect. See Build for iOS.
flutter doctor --android-licenses Accepts the Android SDK licences.
dart run build_runner build --delete-conflicting-outputs Regenerates the generated code (the JSON models and the dependency-injection wiring). Not needed to build: the generated files ship with the source. Run it only after changing an annotated class yourself.

A configuration change never needs flutter clean. Stop the app and build again; a hot restart does not pick up a changed asset.

The web and desktop folders in the package are not targets. The app does not start on the web.

apply_app_config.dart

Makes the build follow assets/config/app_config.json on both platforms, and prints what each platform will use.

dart run tool/apply_app_config.dart [--check] [--release] [--icons]
                                    [--root <dir>] [--allow-template-id]
Flag What it does
(none) Validates the config, then writes iosBundleId and iosTeamId, when the config sets them, into ios/Runner.xcodeproj/project.pbxproj.
--check Changes nothing. Exits 1 when the config is invalid, or when the Xcode project's bundle ID or team differs from the config.
--release Also applies the release-build rules: a baseUrl that is not https:// or a wsBaseUrl that is not wss:// is an error. Without it, a warning.
--icons Also regenerates the Android and iOS launcher icons from assets/icons/app_icon.png, after checking it is a real PNG. Ignored with --check.
--allow-template-id Writes iosBundleId even when it is the template's own ID and the Xcode project already has an ID of yours. Refused without this flag.
--root <dir> The project root. Defaults to the current directory.
--help Prints the usage.

Exit codes: 0 done, 1 something must be fixed, 64 a bad command line.

Only the iOS bundle ID and the Apple team are written, because they are Xcode build settings that signing reads before any build phase runs. Everything else is read by the build itself: Gradle reads appName and androidApplicationId on every Android build, the Apply app_config.json phase writes appName into every iOS build, and the app reads the colours when it starts. Run the tool again whenever you change iosBundleId or iosTeamId, and after taking a new release's Xcode project.

What it checks. Every problem is reported at once, with the same rules the build and the app apply:

Key Rule
baseUrl, wsBaseUrl Required. https:// or http://, and wss:// or ws://, with a host. The unencrypted form is an error only with --release.
appName One line: a tab or line break is an error. A warning above 30 characters (Google Play's title limit) and above 12 (about what fits under an iOS icon).
androidApplicationId Two or more dot-separated parts, each starting with a letter, using letters, digits and underscores.
iosBundleId Two or more dot-separated parts of letters, digits and hyphens. A warning when it is the template's own.
iosTeamId Exactly 10 upper-case letters and digits. A warning when it is the template vendor's team.
primaryColor, buyColor, sellColor, accentColor #RRGGBB or #AARRGGBB. An empty value means the default.

Any of these keys holding a number or true instead of a string in quotes is an error. The tool does not check the other keys; see the configuration reference.

What it prints. A summary in four blocks, then warnings, then the next step:

Applied assets/config/app_config.json

Name
  in the app             "Acme Exchange"
  Android home screen    "Acme Exchange"  (android/app/build.gradle.kts reads appName on every build)
  iOS home screen        "Acme Exchange"  (the "Apply app_config.json" build phase writes it into every build)

Identifiers
  Android applicationId  com.acme.exchange  (androidApplicationId in app_config.json; Gradle reads it on every build, nothing to write)
  iOS bundle id          com.acme.exchange  (iosBundleId; ...)
  iOS signing team       A1B2C3D4E5  (iosTeamId; ...)

Colours (read by the app at start-up)
  primaryColor           #2F6BFF    custom
  buyColor               #0ECE7A    default
  sellColor              #FF5A5F    default
  accentColor            #1890FF    default

Icon
  assets/icons/app_icon.png: 1024x1024 PNG

Next: dart run tool/release_preflight.dart

With --check the first line reads Checked instead of Applied. Read the Identifiers lines before every release: the first two are the IDs the stores know your app by, and the third is the Apple team the iOS app is signed with.

Without --release, an http:// or ws:// address is printed as a warning starting warning: for a RELEASE build:. With --release it is an error, and the error block starts error: assets/config/app_config.json has 1 problem for a release build:.

Release preflight

Reports every way the project is still the template, and the release mistakes that do not stop a build. It builds nothing and changes nothing.

dart run tool/release_preflight.dart                 # the verdict
dart run tool/release_preflight.dart --explain       # the verdict, and the fix for each item
dart run tool/release_preflight.dart --android-only  # skip the iOS checks

--android-only is for a build that never goes to Apple. It first prints release preflight: --android-only, so the iOS checks were skipped, then drops the iOS findings (ios-name-phase-missing, ios-bundle-id-mismatch, ios-team, ios-team-mismatch, ios-push, and the iOS parts of bundle-id and launcher-icon). Every Android check still runs. The flags combine.

A passing run prints release preflight: this build has been differentiated from the template and exits 0. A failing one prints RELEASE PREFLIGHT FAILED — N item(s) still ours, not yours:, one line per finding as [id] what is wrong, and exits 1. Run it before every release build. The installers run it at the end, as advice only. Why the stores make this a requirement is on Differentiation.

ID Reported when
config-missing assets/config/app_config.json does not exist.
config-unreadable The config is not valid JSON.
config-invalid A value the build or the app would refuse, with the same wording as apply_app_config.dart --check. One finding per problem.
app-name appName is not set, or is still a template name.
android-label-hardcoded AndroidManifest.xml has a typed-in android:label, different from appName, that overrides it.
android-label-placeholder-missing The manifest uses ${appName} but build.gradle.kts is older and never sets it, so every Android build fails.
ios-name-phase-missing The Xcode project has no Apply app_config.json build phase, so the iOS name ignores the config.
bundle-id The Android application ID or an iOS bundle ID is still the template's, or the iOS ID cannot be read from the project.
android-id-hardcoded build.gradle.kts has a typed-in applicationId that overrides androidApplicationId.
ios-bundle-id-mismatch iosBundleId is set and the Xcode project signs under a different ID. Every iOS build would stop.
ios-team The Xcode project still signs with the template vendor's Apple team (52K7QMD7KR, from releases up to 5.3.9), or names no team for Release. The package's Xcode project has no team, so this is reported until you set iosTeamId or choose a team in Xcode. If you build only for Android, use --android-only.
ios-team-mismatch iosTeamId is set and the Xcode project names a different team.
release-signing android/key.properties is missing (the release would be debug-signed), or lacks one of storeFile, storePassword, keyAlias, keyPassword.
launcher-icon A generated launcher icon is missing, or is still the template's.
ios-push ios/Runner/GoogleService-Info.plist exists but the project has no push entitlement, so iOS push cannot work. Not reported when you have not set up Firebase for iOS.
android-gradle-missing android/app/build.gradle.kts cannot be read.
android-target-api compileSdk or targetSdk is missing, inherited from Flutter, or below 36.
app-icon assets/icons/app_icon.png is missing or is still the shipped placeholder, which is text, not an image.
app-icon-size The icon is smaller than 1024x1024.
palette The config sets none of the four colours to a colour of your own.
backend-url baseUrl is still the placeholder https://your-backend-url.com, another address the template ships, or http://localhost.
backend-not-encrypted baseUrl is not https:// or wsBaseUrl is not wss://. A release build would stop on it.

It deliberately does not judge taste. It checks that each decision was made, not whether it was made well.

ios_app_identity.dart

The script the Apply app_config.json build phase of the Runner target runs on every iOS build. You do not run it yourself, except to preview.

dart tool/ios_app_identity.dart --dry-run [--root <dir>]

On each iOS build it:

  • writes appName into the built Info.plist (CFBundleDisplayName and CFBundleName), so the tracked ios/Runner/Info.plist never changes;
  • stops the build with an error: line when iosBundleId is set and differs from the bundle ID Xcode is signing, saying which side to change;
  • warns, without stopping, when app_config.json is missing or empty and the build falls back to the example.

--dry-run prints what an iOS build would do, compares iosBundleId with the Xcode project, and exits 1 with error: [dry run] an iOS build would stop here: ... when a Mac build would stop. It needs no Xcode and writes nothing, so it works on Windows and Linux.

The iOS build runs this file from tool/. Keep the tool/ folder in the project, or every iOS build fails.

The installers

setup/installers/install.sh (macOS and Linux) and setup/installers/install.bat (Windows) walk through a first setup. They find the project root on their own.

In order, they:

  1. Check the tools: Flutter, Dart and Git; install.sh on macOS also checks the Xcode command line tools and CocoaPods, and on Linux, clang. install.sh offers to install what is missing where it can; install.bat lists where to download it and stops.

  2. Write the configuration. If app_config.json exists they ask whether to keep it, create a new one or view it. A new configuration asks, in order: site address, WebSocket URL, app name, Android application ID, iOS bundle ID, the four colours, app version, default exchange provider, default trading pair, Stripe publishable key and Google OAuth client ID. They check each address, ID and colour as you type it, then write a complete app_config.json. An http:// site address or ws:// WebSocket URL is accepted, for a local test backend, with a warning that a release build refuses it.

  3. Run flutter pub get, retrying once after flutter clean.

  4. Run dart run tool/apply_app_config.dart.

  5. Set up the icon: copy the PNG you name to assets/icons/app_icon.png and run dart run tool/apply_app_config.dart --icons.

  6. Set up the platforms you choose: Android accepts the SDK licences; iOS (macOS only) runs pod install and reminds you to choose your signing team. A web option is offered; do not use it.

  7. Run code generation with build_runner. This is unnecessary, because the generated code ships, and a failure here is harmless.

  8. Run flutter doctor, then offer a release APK build. install.sh also offers an iOS build on macOS; install.bat also offers a web build, which does not work. They will not start any of these builds while app_config.json has a baseUrl that is not https:// or a wsBaseUrl that is not wss://, because the app would stop on its Configuration Error screen; they say so and point you to flutter run for a debug build. Without android/key.properties the APK is signed with the debug key: fine on a test phone, refused by Google Play.

  9. Run dart run tool/release_preflight.dart --explain as advice, and print the next steps.

Check the file they write before you build for release:

  • Choosing "Create new configuration" replaces app_config.json completely. Any key the installer does not ask about is reset or gone afterwards: it writes "iosTeamId": "", so a team you had set is cleared, and it leaves googleAuthEnabled out.
  • The Google button follows the client ID you give. With googleAuthEnabled left out, the button shows when you entered a Google client ID and stays hidden when you left it empty. If your Android build takes the client ID from google-services.json instead, add "googleAuthEnabled": true.
  • The "app version" answer is only a fallback. The version the app shows comes from the version: line of pubspec.yaml.
  • It writes only keys the app reads. It no longer asks for a CryptoCompare key (market news comes from your server) or about "Coming Soon" features (which modules appear is your server's decision), and it writes none of the keys in Keys the app ignores.

run_tests.sh

The package's test suite, as one command. It is a bash script; on Windows, run it from Git Bash. It needs flutter on the PATH.

./run_tests.sh               # static analysis, then every suite
./run_tests.sh contract      # only test/contract
./run_tests.sh --no-analyze  # skip static analysis
./run_tests.sh --help

It runs flutter pub get (the one stage that stops the run on failure), flutter analyze, then flutter test on each suite in this order:

Suite What it covers
test/core The request layer, the config and error mapping.
test/services Service-level parsing.
test/tool apply_app_config.dart, the iOS build phase and packaging.
test/compliance The store-policy gates: differentiation, target API and similar.
test/features The bulk of the suite.
test/contract The app's endpoints checked against the platform's backend source. Without that source next to the project, as in a downloaded copy, these suites skip their checks.
test/e2e The full sign-in stack against a fake server started inside the test. It needs no backend.

Every stage runs even after one fails. The last lines say ALL GREEN, GREEN, for what ran (with a NOT RUN: line naming any suite folder that is missing), or FAILED: with the stages that failed. The exit code is 1 when anything failed.

The restricted-modules test is written to pass both ways. To check a build with the flag on:

flutter test --dart-define=STORE_RESTRICTED_MODULES=true test/features/dashboard/opt_in_modules_test.dart

flutter_launcher_icons

The icon generator, configured in the flutter_launcher_icons: section of pubspec.yaml. It reads assets/icons/app_icon.png and writes every Android and iOS launcher icon, and the web icons. It makes no Android adaptive icon: the android_adaptive_icon: block in that section is not a setting the generator's version (0.13.1) reads.

dart run flutter_launcher_icons

Prefer dart run tool/apply_app_config.dart --icons. It runs the same generator, but first refuses the shipped placeholder and warns about an icon that is not square or is smaller than 1024x1024. Replacing the PNG changes nothing until one of the two has run; the preflight reports stale icons as launcher-icon.

What the build prints

Three lines come from android/app/build.gradle.kts on Android builds. Flutter's normal output filters them out, so look for them in a --verbose build:

Line Meaning
App identity from assets/config/app_config.json: name="..." applicationId=... The name and application ID this build uses. When it names app_config.example.json instead and ends (assets/config/app_config.json is missing or empty, so the tracked template was used), the build read the example config.
google-services.json found - Firebase push ENABLED / google-services.json not found - Firebase push DISABLED. ... Whether this build has Android push.
WARNING: android/key.properties not found — signing the release build with the DEBUG keystore. ... This release build cannot go to Google Play.

Gradle stops the build, whatever the verbosity, on a config it cannot use: assets/config/app_config.json is not valid JSON (at line N column M), an appName or androidApplicationId that must be a string in quotes, an appName that contains a control character, or an androidApplicationId that is not a valid Android application ID. More build messages are on Build for Android and Troubleshooting.