KESTREL docs
v1.0.0
Get help
● Set up · Build and release

Mobile and web apps
build them for your server and publish them.

The same Flutter app runs on Android, iOS, macOS and the web. You compile your server address into it, sign it with your own accounts and publish it under your own name.

AndroidiOSmacOSWebFlutter 3.41

01Set the server address

The app talks only to your server. Its default address is compiled in at build time (app/lib/config/app_config.dart):

flutter build appbundle --release --dart-define=KESTREL_API_URL=https://trade.example.com

Use the same --dart-define=KESTREL_API_URL=… on every build command below, with your own domain (the PUBLIC_URL of your server). Without it the default is http://localhost:8080, which only works for local development. You can also change the default in app_config.dart itself (defaultValue of defaultServerUrl).

Users can change the address in the app too: on the sign-in screen (the Server line) or under Settings, Server, which has a Test connection button that calls /health. The address must start with https:// (http:// only for a local test server).

The web app is built for you

With the Docker install you do not build the web app yourself: app/Dockerfile.web builds it with your PUBLIC_URL and Caddy serves it. ./update.sh rebuilds it.

02Before the first build

cd app
flutter pub get
flutter analyze
flutter test

Then change the app name, the application ID / bundle ID and the icons to your own (White label). The application ID cannot be changed after the first release on Google Play.

03Android

  1. Create an upload keystore (once, keep it safe; losing it means you cannot update the app):
    keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
  2. Create app/android/key.properties (never commit it; it is in .gitignore):
    storeFile=/absolute/path/to/upload-keystore.jks
    storePassword=your-store-password
    keyAlias=upload
    keyPassword=your-key-password
    Without this file release builds are signed with the debug key, which works for testing but cannot be published (app/android/app/build.gradle.kts).
  3. Build the bundle:
    flutter build appbundle --release --dart-define=KESTREL_API_URL=https://trade.example.com
    The file is build/app/outputs/bundle/release/app-release.aab. For a direct install use flutter build apk --release ….
  4. Publish in the Google Play Console: create the app, upload the .aab, fill in the store listing, the data safety form and the content rating.

The app supports Android 7.0 (API 24) and newer and targets API 36.

04iOS

  1. Install the pods: cd ios && pod install && cd ..
  2. Open ios/Runner.xcworkspace in Xcode. Under Runner, Signing & Capabilities, choose your own Team (the project ships with an empty team) and set your bundle identifier.
  3. Build the archive:
    flutter build ipa --release --dart-define=KESTREL_API_URL=https://trade.example.com
    The result is in build/ios/ipa/.
  4. Upload with the Transporter app or Xcode's Organizer to App Store Connect, then submit for review.

The app supports iOS 15.0 and newer.

05macOS

  1. Install the pods: cd macos && pod install && cd ..
  2. Open macos/Runner.xcworkspace and choose your Team under Signing & Capabilities.
  3. Build:
    flutter build macos --release --dart-define=KESTREL_API_URL=https://trade.example.com
    The app is in build/macos/Build/Products/Release/. Distribute it through the Mac App Store, or sign and notarise it for direct download.

The app supports macOS 12.0 and newer. It runs in the App Sandbox with the outgoing network permission (macos/Runner/Release.entitlements).

06Web (without Docker)

flutter build web --release --dart-define=KESTREL_API_URL=https://trade.example.com

Upload build/web/ to any static host. If the web app is on a different address than the server, add that address to CORS_ORIGINS (and WS_ORIGINS) on the server. Serve index.html, flutter_bootstrap.js and flutter_service_worker.js with Cache-Control: no-cache so users get updates, as deploy/Caddyfile does.

07Publishing checklist

  • Your own app name, application ID / bundle ID and icons (White label).
  • Built with your server's address.
  • Terms of service and privacy policy online, linked in the store listing.
  • Store review notes: give the reviewer a test account on your server. A paper account lets them try trading without funds.
  • Check each store's rules for trading apps in the countries you publish in. Some stores and countries need extra documents or licences for financial apps.
  • Push set up, if you use it (Push notifications).
Cost: store accounts

Apple Developer Program: yearly fee to Apple. Google Play: one-time registration fee to Google. Neither is included.

08Fonts need the internet

The app's fonts (Geist, Geist Mono, Inter) are loaded through the google_fonts package at run time (app/README.md). The app needs a connection to your server anyway; the fonts also need access to Google Fonts. To bundle the fonts inside the app instead, follow the google_fonts package's instructions for bundled font files.