Security
Flutter
Why --dart-define Won't Hide Your API Keys (+ a Hands-On Demo)
May 10, 2025

TL;DR: Passing secrets to Flutter with `--dart-define` or `--dart-define-from-file` makes the build _feel_ clean (no keys committed to GitHub) but the values end up **hard-coded** in every release artifact. Anyone with 30 seconds and basic tools can recover them.
The quick misconception
--dart-define
was built for build-time configuration, not for long-lived secrets. When you build using --dart-define
the string value of the variable is stored verbatim inside the generated snapshot/libapp.so, the minified JS bundle, or the desktop binary.
You can open the file using the bash command strings
or grep
and read it in plain text. Developers on StackOverflow have verified this since 2022 by disassembling real APKs. (Source)
Set up a quick demo
Create a tiny app that reads
String.fromEnvironment("API_KEY")
and uses the value (i.e, a call to an API)

Define the variables file vars.json

And finally, build the app using flutter build web --release
, for our purposes, we decided to go with the web platform to showcase how easy it is to extract the API key.

Step-by-step extraction of the key
Open the build output file, which should be in build/web/main.dart.js
and let's look for our key using any editor of our choice.

The key is right there, for anyone to extract and abuse.
Why obfuscation doesn't save you
Flutter's --obfuscate
only renames symbols. Guardsquare's reverse-engineering research shows the string literals themselves remain intact; obfuscation only hides classes/function names, not data.
What actually works for secrets
Put the key on a backend/proxy
The client makes an authenticated call to the server; the server holds the secret and forwards the call.
Use a Proxy-as-a-Service
Rather than building a backend/proxy server, you can use a managed service that stores your keys, securely injects the keys server-side, and forwards the request.
Some managed services to consider: Proxana (proxana.dev) and AIProxy (www.aiproxy.com). However, AIProxy only works on Swift SDKs, which makes targeting a cross-platform goal not feasible.
Obfuscate and wish for the best
If you truly need to have secrets in your app that you can't put on a backend/proxy. Then, obfuscating and wishing for the best is your only option.
Key take-aways
✅
--dart-define
cleans up your repo but doesn't protect your secrets.✅ All release artifacts contain your constant values in plain text.
⛔ Obfuscation slows attackers down; it doesn't erase the bytes.
🚀 Offload the secret to a server, proxy, or zero-trust token system instead.
Stop shipping your production keys with the app, ship requests that let the server handle them securely.