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
- Build the client. In the [rdvmedecins_flutter] folder:
The console displays:
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.
- 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:
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
- Build the app, specifying the server address:
The console displays (for a APK built without [--dart-define]):
(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.
- Install it. With the phone connected to the computer (USB cable, USB debugging enabled):
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.”
- 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].