Flutter Web — Runtime Docker variables
Docker containers are now a common way to deploy web applications and services. Containers help to reduce maintenance and environment…
Docker containers are now a common way to deploy web applications and services. Containers help to reduce maintenance and environment configuration time. The ability to pass variables to containers allows us to configure them without having to rebuild containers. When we run the Flutter Web application as a container, it is helpful to have variables to set endpoint addresses, environment type: test, staging, production…, and other useful variables. There are two kinds of Docker variables: ARG variables are available during the build, and ENV variables are available while we are running containers.
Passing some variables to the container as parameters on startup is very convenient. For example, we could use the same Docker image for the web application in the test and staging environments, passing different API endpoints and environment type variables.
However, the Flutter Web application could not access the variables provided as Docker ENV variables because the application scripts run on the client side and do not have access to the Docker variables. The only solution, for now, is to update the Flutter Web application files during the startup of the Docker container. For example, we could create an environment.json asset file in the Flutter Web project and update it with Docker ENV variables on container startup.
{
"app_environment": "production",
"api_endpoint": "api.app.domain",
"api_port": 8080
}When the Docker container starts, the environment.json and all related Flutter files can be updated with a simple Dart program prepare_app_to_start.dart, here is part of that program:
// Compose app version by adding build_number and environment type
final versionJsonFile = File(versionJsonPath);
final versionJson = jsonDecode(await versionJsonFile.readAsString());
final appVersion = '${versionJson['version']}b${versionJson['build_number']}${appEnvironment[0]}'.trim();
// Save environment json file
final envFile = File(envJsonPath);
final envFileContent = const JsonEncoder.withIndent(' ').convert(envJson);
final envFileMd5 = md5.convert(utf8.encode(envFileContent)).toString();
await envFile.writeAsString(envFileContent);
* * *
// Update flutter_service_worker.js file by specifying environment.json MD5 hash
final workerJsFile = File(workerJsPath);
final workerJsContent = await workerJsFile.readAsString();
final workerJsReplaced = workerJsContent.replaceAll(
RegExp('"assets/asset/$envFileName": "[0-9a-f]{32}"'),
'"assets/asset/$envFileName": "$envFileMd5"',
);
await workerJsFile.writeAsString(workerJsReplaced);As you can see, we also update the flutter_service_worker.js script as well to set the actual MD5 value for the environment.json asset file.
You can read more about the Flutter script files update the previous article ‘Flutter Web — “New website version is available, press Update to refresh”’.
The Flutter Web application Docker image runs entrypoint.sh on startup to run the prepare-app-to-start utility described above:
FROM nginx:alpine as production
COPY --from=build_dart /runtime/ /
COPY --from=build_dart /app/tool/prepare-app-to-start /app/bin/
COPY --from=build_dart --chmod=0755 /app/tool/entrypoint.sh /app/bin/
COPY --from=build_web /home/build/web /usr/share/nginx/html
EXPOSE 80/tcp
ENTRYPOINT ["/app/bin/entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]entrypoint.sh script runs the prepare-app-to-start utility and then executes the ‘nginx -g daemon off;’ command passed as a script parameter from the Dockerfile:
#!/bin/sh
/app/bin/prepare-app-to-start app_environment=$APP_ENVIRONMENT app_version=$APP_VERSION
# Exec the CMD from the Dockerfile
exec "$@"The Flutter Web application Docker image can be built and deployed using GitHub Actions or another CI/CD tool. You can find a GitHub Actions example in the example project repository as well.
If you prefer Firebase CI/CD, you can pass build-time environment variables as ‘-dart-define’ parameters to the ‘flutter build’ command. This approach is less flexible, but still covers all common cases.
I hope you find this approach useful for your applications. Perhaps the Flutter team will implement a simpler approach in the future, or at least fix application reloading issues.
🚀 Try It
web-app-update is open-source and ready-to-use. Go build something amazing.
- ⭐ Star on GitHub – Support the project
- 📘 Read Documentation – Integration guide & API reference
📚 Further Reading
- Flutter Documentation — Flutter deep dive
- Awesome Flutter — useful links
🤝 Let's Connect
I'm a software engineer building high-performance systems. If you have questions or want to clarify details, feel free to reach out.
Blog · GitHub · LinkedIn · X / Twitter · Email
with ❤️ Dmitrii Zusmanovich