Skip to content

12. Deployment

During development, the application runs under the control of [flutter run]: in “debug” mode, which is slower, with hot reloading and checks ([assert]) performed by [Flutter]. To deploy it, we build it in “release” mode: optimized code, without development tools. We build one version per platform.

12.1. The web version

  1. Build the client. In the [rdvmedecins_flutter] folder:
flutter build web --no-web-resources-cdn

The console displays:

1
2
3
4
5
6
7
Compiling lib\main.dart for the Web...
Wasm dry run succeeded. Consider building and testing your application with the `--wasm` flag. See docs for more info: https://docs.flutter.dev/platform-integration/web/wasm
Use --no-wasm-dry-run to disable these warnings.
Expected to find fonts for (MaterialIcons, packages/cupertino_icons/CupertinoIcons), but found (MaterialIcons). This usually means you are referring to font families in an IconData class but not including them in the assets section of your pubspec.yaml, are missing the package that would include them, or are missing "uses-material-design: true".
Font asset "MaterialIcons-Regular.otf" was tree-shaken, reducing it from 1645184 to 9004 bytes (99.5% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Compiling lib\main.dart for the Web...                             70,5s
√ Built build\web

Two warnings, with no consequences: compilation into [WebAssembly] would be possible (see below); a library in [Flutter] references icons from the [iOS] ([CupertinoIcons]) style, which the application does not use.

The [dart2js] compiler has compiled the entire application ([Dart], [Flutter], and the packages) into [JavaScript], in the [build/web/] folder:

  • [index.html]: THE application page, which loads [flutter_bootstrap.js], then [main.dart.js] (3.5 MB, approximately 1 MB once compressed by the server): OUR code, along with all the code from [Flutter] and the packages, optimized. [Flutter] does not generate HTML: the application draws each pixel itself on a canvas, using the same engine as on the phone;
  • [canvaskit/]: this rendering engine ([Skia], compiled into [WebAssembly]: [canvaskit.wasm], 7 MB). Without the [--no-web-resources-cdn] option, it would be downloaded from a [Google] server; with it, it is included with the application, which no longer depends on any external site;
  • [assets/]: the resources from [pubspec.yaml] (dictionaries, fonts). The icons are “tree-shaken”: only those used by the application are kept in the icon font;
  • Upon first load, the browser downloads a few megabytes (primarily the rendering engine) and then caches them. This is the main weakness of [Flutter] for the web: it is better suited for applications (which are opened and used for extended periods) than for websites (which are browsed briefly).

The [--wasm] option compiles the application as [WebAssembly] rather than [JavaScript]: it is faster in browsers that support it (recent versions of [Chrome], [Edge], and [Firefox]); other browsers automatically receive the [JavaScript] version.

  1. Place it on the server. Copy the CONTENTS of the [build/web/] folder to a folder named [client/] at the root of the JSON server ([rdvmedecins-nestjs-json/client/index.html], [rdvmedecins-nestjs-json/client/main.dart.js]...), then restart the server. It displays:
[Nest] LOG [RdvMedecins] web client served from .../rdvmedecins-nestjs-json/client
[Nest] LOG [RdvMedecins] JSON server started: http://localhost:8080/api

The complete application is now available at http://localhost:8080. The server serves the client’s files and returns [index.html] for any other address outside of /api: reloading the /schedule page works (it is the client’s router that displays it). The server’s [src/web/client-spa.ts] file handles this task. The application is served by the API server: the relative address of [config.dart] ([''], or [/api/...]) is correct.

You can also try the version built without the JSON server, using any file server, but the /api requests will fail.

12.2. The Android version

  1. Build the app, specifying the server address:
flutter build apk --dart-define=SERVER=http://192.168.1.20:8080

The console displays (for a APK built without [--dart-define]):

1
2
3
4
Running Gradle task 'assembleRelease'...
Font asset "MaterialIcons-Regular.otf" was tree-shaken, reducing it from 1645184 to 3400 bytes (99.8% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Running Gradle task 'assembleRelease'...                          193,4s
√ Built build\app\outputs\flutter-apk\app-release.apk (53.5MB)

(A few warnings regarding [Gradle] and [Java], which had no impact, have been removed.) The first build takes a long time: [Gradle] downloads its own tools and libraries; subsequent builds are much faster.

The [Dart] compiler translated the application into machine code for the three processor families of [Android] phones (ARM 32-bit, ARM 64-bit, x86 64-bit), and the [Android] build tool ([Gradle]) packaged it, along with the [Flutter] engine, into a APK file: the [Android] application installation format. It contains three copies of the application and its engine—one per processor family—which explains its size. [--split-per-abi] would generate three smaller APK files, one per processor family; an “App Bundle” (see below) allows the store to deliver only what each phone needs.

  1. Install it. With the phone connected to the computer (USB cable, USB debugging enabled):
flutter install

You can also copy the APK file to the phone and open it: [Android] will then ask you to allow the installation of apps “from unknown sources.”

  1. For a real release:
  • the app must be signed with a developer-specific key: the project generated by [flutter create] signs the “release” version with the debug key, which is sufficient for testing but not for a store. The [Flutter] documentation (“Build and release an Android app”) describes how to create the key and configure [Gradle];
  • [Google Play Store] expects an “Android App Bundle” rather than a APK: [flutter build appbundle];
  • The app name and icon are configured in [android/app/src/main/AndroidManifest.xml] ([android:label]) and [android/app/src/main/res/mipmap-*/];
  • The server must be set to HTTPS (passwords are shared), and the line [android:usesCleartextTraffic="true"] is removed from the manifest.

12.3. The production server

In actual production, don’t forget the [.env] file on the server:

  • a long, random [JWT_SECRET];
  • an empty [CAPTCHA_TEST];
  • [COOKIE_SECURE=true] if the site is served via HTTPS (it must be). The mobile client sends the cookie regardless of this option: it is the browser that enforces it;
  • [TRUST_PROXY=true] if the server is located behind a proxy ([nginx]...), so that the attempt limit sees the clients’ actual IP address.

We then launch the compiled server: [npm run build], followed by [npm run start:prod].