One of the features of Flutter – open-source mobile app development framework created by Google – is its ability to support multiple environments, flavors, which enable developers to deploy their apps in various configurations. It has also two run modes – when the app is under development and when it is in the hands of users. In this article, I will delve into the world of run modes, environments, and flavors in Flutter and explore how they could be archived.
Target Platforms
Flutter started with the Mobile Platforms – Android and iOS. Incrementally they’ve expanded to stable release – for Web, Desktop and Embedded. The move to all platforms is so slow – because of all the different technological and platform choices – everywhere.
- on Android you could build apks or app bundle (flutter build apk(s) or flutter build appbundle)
- flutter run -d/build ios from iOS exited iPadOS. As far as I read – it is supported, but who knows how it will evolve – with OS specific stuff.
- Linux has a lot of different distributions and packaging formats – Debian/Ubuntu, Snaps, Fedora, etc. flutter run -d linux
- Windows has – Win32, Metro style apps, (UWP) apps… flutter build windows
- flutter build web – Web has plain JS/CSS, and is incrementally moving to WebAssembly…
It is responsibility of the Flutter team to choose and stick to some specific options.
Run Modes
No matter what settings you’ve set up on the belows parameters – Flutter supports two run modes: Debug Mode and Release Mode. They are executed with:
- flutter run -d [your platform – ios, android, chrome/edge, windows, macos, linux] – Debug Mode:
When you run your app in this mode your flutter code is INTERPRETED inside a Dart Virtual Machine. A debugging socket is attached to your app – so you could do your work as a software developer. This is the default mode used for testing and debugging purposes. In debug mode, the app runs with additional logging and checks to ensure that it behaves as expected.
- Release Mode: Once an app is deemed ready for production, it can be built in release mode. The resulting binary is optimized for performance and security.
flutter run --release
or flutter build [your platform-format - apks/appbundle, ios...]
.
-
- With the first command your app is started on your device, but in compiled mode – without all attachments used for coding. If you close your app when started in debug – it will not start from the device (but from the development computer). If you close the app packaged in release – you will be able to open it again (from the device).
- With the second command your app is not actually started, but an installation package or executable file is produced. You then may run it or publish it to the application stores.
So far all commands and options above come with the Flutter SDK and the platform specific tools. Let’s involve into items you could control:
Environments in your Flutter
The way I understand it – environments is about targeting different Back-End Servers and different configurations of the external services that are not just – the official, public – visible to the 99.99% of the users. Flutter supports multiple environments.
- Development Environment: This is the default environment used during development.
- Staging Environment: A staging environment can be used for testing and logic verification before deploying the app to production.
- Pre-Prod Environment – in many cases corporations delegate work to outside companies. The outside company has the first two setups. It is important for the recieving company to also have – similar environments – in their control – to verify quality of delivery.
- Production Environment: This is the final environment where the app will be deployed to users.
In Flutter Environments may be set up with several strategies that mostly involve – having an object – that holds the environment specifics.
-
- custom enum
- object from package like https://pub.dev/packages/flutter_flavor.
- choosing what is selected with command line / launch arguments:
- https://dart.dev/tutorials/server/cmdline
- https://pub.dev/packages/launch_args
- https://stackoverflow.com/questions/55004302/how-do-you-pass-arguments-from-command-line-to-main-in-flutter-dart
- different dart entry point (different main.dart file) flutter run -t lib/main_test.dart. -t means –target=[dart_file]
This is variation in the Flutter Code – with the same platform code beneath. If you stop here – you will be able to have installed on your devices – only one installation of your app – targeting the environment – the last installed.
- Sometimes configurations may need to be passed to the native platform.
- Sometimes you may want to have installed – several versions of the app – targeting different environments – so you could compare functionality instantaneously.
In such cases native platform flavors may need to be implemented.
Flavors is about configuration in Native Platforms
Flutter flavors allow you to build and deploy different versions of your app with distinct configurations. Most platform require to have app identifier that distinguishes your app – so this will need to be different.
flutter_flavorizr is the package https://pub.dev/packages/flutter_flavorizr that creates the variations. It requires from you to
- run – flutter pub add dev:flutter_flavorizr
- create a flavorizr.yaml file in your project’s root – containing
flavors: prod: app: name: "Image Client" android: applicationId: "com.programtom.image_client" ios: bundleId: "com.programtom.image_client" macos: bundleId: "com.programtom.image_client" dev: app: name: "Image Client Dev" android: applicationId: "com.programtom.image_client.dev" ios: bundleId: "com.programtom.image_client.dev" macos: bundleId: "com.programtom.image_client.dev"
- run flutter pub run flutter_flavorizr
It will make changes
- in your Flutter Code
- in the native setup
In some way – flavors are bigger environment splitters. It is good idea to have done that at the beginning of your project, because it may change some of the files (like the main.dart). If you are NOT running the command 3) on macOS – it may not succeed changing all the necessary files, as it requires ruby, gem, xcode commands.
After it – run your app with –flavor=prod/dev and map it with the corresponding environment in flutter (either by run variables or arguments or the correct main_xxx.dart). Example changes needed in an already coded app could be seen here: https://github.com/tomavelev/image_client/commit/69777eb9ed79e95bc179d2ee51a0f4bd939747ba