View Categories

Building the Android App

5 min read

This guide builds the Android version of the Academy & Exam OS student app for your institute. Complete Flutter Mobile App — Setup & Configuration first: you need a validated production profile (config/production.local.json).

Prerequisites:

  • Flutter 3.44.x with Dart 3.12, and flutter doctor showing a working Android toolchain;
  • the Android SDK with the API 36 platform and build tools, and JDK 17 or newer;
  • a validated production profile;
  • a Google Play developer account if you will publish on Google Play.

Run every command from the Flutter-App-Source/ folder.

Step 1: Set the application ID, name and icon #

  • Application ID: set ANDROID_APPLICATION_ID in the profile (reverse-DNS, for example org.myinstitute.learn). If it is not set, release builds use com.osdots.academyexam. Choose your own ID before the first store upload — it cannot change afterwards. Debug builds always append .dev, so a development install never replaces a release install.
  • App name: the launcher label is SITE_NAME from the profile.
  • Icon: replace the launcher icons in android/app/src/main/res/mipmap-*/ic_launcher.png with your institute’s icon. The shipped icons are neutral placeholders.

Step 2: Create your upload key #

Release builds must be signed with your own upload key; the Android debug key is refused. Create a keystore once:

keytool -genkeypair -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload

Then create android/key.properties with the location and passwords of your keystore:

storeFile=/absolute/path/to/upload-keystore.jks
storePassword=<store password>
keyAlias=upload
keyPassword=<key password>

Important: Keep the keystore and its passwords private and backed up. If you lose them, you cannot publish updates to the same app on Google Play. Never share or commit android/key.properties.

Step 3: Build the release #

Build an Android App Bundle for Google Play:

flutter build appbundle --release --dart-define-from-file=config/production.local.json

The bundle is written to build/app/outputs/bundle/release/app-release.aab.

For a directly installable test build with the same configuration, build an APK:

flutter build apk --release --dart-define-from-file=config/production.local.json

The release gate #

The Android project refuses to build a release unless:

  1. android/key.properties names an existing keystore with all four values; and
  2. the build received a production profile that passes validation (the validator runs automatically inside the Gradle build).

A build without a profile, with a development profile or with the unedited template stops and prints the reason. Debug and profile builds are not gated; they are for development and label themselves on screen.

Step 4: Verify on a device #

  1. Install the APK on a test device (for example adb install app-release.apk), or share the build through Google Play’s internal testing track.
  2. Open the app.

Expected result: the app shows your institute name, loads the published test catalogue from your website over HTTPS, and lets students sign in with their website accounts.

  • “Cannot reach …” — the device cannot reach API_BASE_URL.
  • “This app does not match the configured institute” — DEPLOYMENT_ID or BRAND_KEY belongs to another installation.

Step 5: Publish on Google Play #

Upload app-release.aab in the Google Play Console under your own developer account and complete the store listing, content rating and data-safety information there. Google’s review, fees and policies apply to your account; they are not part of Academy & Exam OS.

When your listing is live, add its address under Mobile / App Store Settings on your website so the homepage app showcase shows the Google Play button. See Website & CMS.

Troubleshooting #

SymptomCause and fix
Release builds need android/key.properties …Create the signing configuration of Step 2. The debug keystore and the alias androiddebugkey are refused.
Release builds require a validated production profile …Pass --dart-define-from-file=config/production.local.json.
The production profile did not validate: followed by codesFix the listed keys; run dart run bin/validate_profile.dart config/production.local.json to check quickly.
[api_host]The host is an IP address, a single word or a reserved / local / example name (.local, .test, .example, localhost …). Use your institute’s real HTTPS domain.
[deployment_id]The value is not the UUID your website reports, or is still the template placeholder.
App shows This app does not match the configured instituteDEPLOYMENT_ID or BRAND_KEY differs from your website’s /api/v1/bootstrap answer. Rebuild with the right values.
App shows Cannot reach … or A secure connection could not be verifiedThe device cannot reach API_BASE_URL, or your website’s TLS certificate is not trusted by the device. Self-signed certificates are never accepted.
App shows This build is not configured for releaseA release build was produced without a valid profile (only possible when the build gate was bypassed). Rebuild through the gate.
Gradle on Windows: Could not close incremental caches … different rootsThe project and the pub cache are on different drives. Keep the project on the drive of the pub cache (or set PUB_CACHE to a folder on the project’s drive) and rebuild.
Unable to open file … hook.dill for writing snapshot on the first buildA file-locking tool such as an antivirus scanner held a new file. Run the same command again.
Google button missingOne of the Google sign-in conditions is not met. See the Google section in Flutter Mobile App — Setup & Configuration.

Related documents #

Watch the Video Tutorial #

This topic is also covered in Academy & Exam OS Video Tutorial 11: Flutter App Setup & Android Build (watch on YouTube). All Academy & Exam OS video tutorials.