Obfuscating Flutter apps for release
By Charlin Joe · 13 November 2025
Obfuscation renames Dart classes, methods, and fields in the compiled binary to meaningless strings, making reverse engineering significantly harder. It's an important step for production Flutter apps, especially those handling payments or sensitive data.
What obfuscation does
Without obfuscation, the Dart snapshot contains readable names:
OrderRepository.createOrder()
CartBloc._calculateTotal()
PaymentService.processCard()
With obfuscation:
a.b()
c._d()
e.f()
This slows down (but does not prevent) reverse engineering. It's one layer in a defense-in-depth strategy.
Enabling obfuscation
# Android
flutter build apk \
--obfuscate \
--split-debug-info=./debug-info/android
flutter build appbundle \
--obfuscate \
--split-debug-info=./debug-info/android
# iOS
flutter build ipa \
--obfuscate \
--split-debug-info=./debug-info/ios
# Combined in a script:
BUILD_DIR="./debug-info/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BUILD_DIR"
flutter build appbundle --obfuscate --split-debug-info="$BUILD_DIR/android"
flutter build ipa --obfuscate --split-debug-info="$BUILD_DIR/ios"
The debug-info folder
The --split-debug-info directory contains the symbol mapping needed to understand obfuscated crash reports. Store this securely. You need it to:
- Symbolicate crashes from Crashlytics
- Decode stack traces from Sentry
- Debug production issues
Never commit this folder to your main repository. Store in a separate secure location or upload to your crash reporting service.
Symbolicating crash reports
Obfuscated crash stacks look like:
#00 abs 0x000000000063c54e _kDartIsolateSnapshotInstructions+0x6c554e
#01 abs 0x0000000000595e13 _kDartIsolateSnapshotInstructions+0x555e13
To symbolicate:
# With flutter tools
flutter symbolize \
--debug-info=./debug-info/android/app.android-arm64.symbols \
--input=./crash.txt
# Output:
# #00 OrderRepository.createOrder (package:myapp/data/repositories/order_repository.dart:42)
Sentry integration
Sentry can symbolicate automatically if you upload debug symbols:
npm install -g @sentry/cli
# Upload Android symbols
sentry-cli upload-dif ./debug-info/android \
--org your-org \
--project your-project
# Upload iOS dSYM
sentry-cli upload-dif ./debug-info/ios \
--org your-org \
--project your-project
In CI:
- name: Build with obfuscation
run: |
flutter build appbundle \
--obfuscate \
--split-debug-info=./debug-info
- name: Upload symbols to Sentry
run: |
sentry-cli upload-dif ./debug-info \
--org ${{ secrets.SENTRY_ORG }} \
--project ${{ secrets.SENTRY_PROJECT }}
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
Firebase Crashlytics
Crashlytics automatically symbolicate if you include the Firebase Crashlytics Gradle plugin:
// android/app/build.gradle.kts
plugins {
id("com.google.firebase.crashlytics")
}
The plugin uploads debug symbols automatically on each build.
Android ProGuard
For additional Android-level obfuscation (beyond Dart obfuscation), enable R8:
// android/app/build.gradle.kts
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
Add Flutter-specific ProGuard rules:
# proguard-rules.pro
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
Limitations
Obfuscation does NOT prevent:
- Decompiling the app binary
- Extracting hardcoded strings (API keys, URLs)
- Dynamic analysis (running the app in a debugger or emulator)
- Observing network traffic
For secrets, use dart-define from CI secrets rather than hardcoding in source. For API protection, enforce authorization on your backend.
Common pitfalls
Not saving the debug-info folder. If you lose the symbol files, production crash reports become unreadable. Archive them alongside each release.
Obfuscating debug builds. Obfuscation makes debugging impossible — don't apply it to debug or profile builds.
Using --split-debug-info without --obfuscate. You can use --split-debug-info alone (just splits symbols without renaming) but don't confuse this with obfuscation. Both flags together provide the full benefit.
Sign in to like, dislike, or report.