How to separate test and production environments in Flutter
TL;DR
This article shows how to effectively use different environments for developing/testing and for releasing a production app in Flutter.
An environment may contain in-app variables such as the backend URL your app needs to connect. But there are also platform dependent build settings that need to differ between a test app and a production app such as a different binary name, bundle ID and more.
The latter is usually done using build variants, or sometimes called Flavors, but since this feature is not supported on all platforms on Flutter I’d like to propose a simple and effective alternative.
The approach show here works on all platforms Flutter supports:
- web
- macOS
- Windows
- Linux
- iOS
- Android
Getting the demo project
This article uses a demo project that is hosted here:
Please clone the repo to try out the approach.
In order to make use of this demo project, you need to be accustomed to multi-platform Flutter development. This project is ready to run on all of the platforms above.
Building and running the app in test mode
Test mode means that your app is configured as follows:
- Connects to a test backend
- Uses a test bundle ID / application ID
- Uses an executable file name with clearly marks it as a test binary
- Stores its local configuration settings in a test area, different from production use
- Registers a protocol handler (for starting a desktop app from a url) that is different from production use
- Registers universal links (So your mobile app is opened when a user clicks a link representing your web app) which are different from production use
- Registers credential handling that is different from production use
The project is running and building a test application per default. So just run the regular flutter command and choose the device:
flutter run
You now have a client running that is called EnvironmentDemoTest which shows a test banner in the top left corner.
If you click on the little plus, an api call to a sample “test” backend is done and the result is being displayed.
(The test backend gives facts abouts cats 🐈)
If you want to build a macOS test app, just run
flutter build macos
You may also notice that the Application file name is EnvironmentDemoTest.app
This works for all other platforms as well!
Building and running the app in production mode
Switching from test mode to production mode is done into two steps:
Step 1: Providing a parameter to use a different app internal environment
For everything that needs to be adapted within your dart code, you may use a variable within your Environment.
Please take a look at environment.dart — it is fairly well documented and includes examples of which variables need to differ between test and production mode. This demo project only makes use of a differing backend host name.
If you want to run your app using a production environment do this:
flutter run --dart-define=environment=production
You now have a client running that is still called EnvironmentDemoTest but it no longer shows a test banner in the top left corner.
If you click on the little plus, an api call to a sample “production” backend is done and the result is being displayed.
(The production backend gives proposals about interesting things to do 🏖️)
Why is the app still called EnvironmentDemoTest? That’s because the application name is a setting that is not dependent on your dart code but instead defined in your platform dependent configuration files. Check the next step!
Step 2: Converting the project into release mode
Since there is no Flavor support in Flutter for macOS we actually need to change files to adapt certain platform specific settings which should differ between a test app and a realeasable production app.
I have come up with a very pragmatic approach which honors the fact that most of the times a developer builds an app, it will be for our all so beloved develop-build-test cycle. This means the test scenario is way more often used than the production scenario!
So my approach advises to leave the git repo in a state where it always builds test applications. In order to convert your repo to a release candidate, you should create a staging (or stable) branch and then (and only then) run the following command:
./change2production.sh
You may run this command on macOS or Linux machines. However it changes the platform settings for all platforms!
This command changes files in this repo! It replaces all test strings with their production counterparts in the platform dependent sub directories:
- ios
- macos
- web
- windows
- android
- linux
Here is a table that shows the replacements:
If you are on a branch that has been created for releasing a version of your app, then you may commit the changes introduced by this script. Make sure your your main/master branch stays on the test configuration!
Building a production release candidate
In your release branch where you have run change2production.sh you may now create a production binary by using the environment parameter:
flutter build macos --dart-define=environment=production
The newly built binary can be uploaded to the app store. You only need to adapt the version number and the build number where applicable. This works for all other platforms as well!